mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-15 00:41:55 +00:00
- moved tab completion code into 'common' as well.
This commit is contained in:
parent
242a70e610
commit
820b37721b
7 changed files with 702 additions and 621 deletions
|
@ -876,6 +876,8 @@ set (PCH_SOURCES
|
|||
common/console/c_consolebuffer.cpp
|
||||
common/console/c_cvars.cpp
|
||||
common/console/c_dispatch.cpp
|
||||
common/console/c_commandbuffer.cpp
|
||||
common/console/c_tabcomplete.cpp
|
||||
common/console/c_expr.cpp
|
||||
common/utility/engineerrors.cpp
|
||||
common/utility/i_module.cpp
|
||||
|
|
343
source/common/console/c_commandbuffer.cpp
Normal file
343
source/common/console/c_commandbuffer.cpp
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
** c_commandbuffer.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** Copyright 2010-2020 Christoph Oelckers
|
||||
** 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 "c_commandbuffer.h"
|
||||
#include "v_draw.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "v_font.h"
|
||||
#include "utf8.h"
|
||||
|
||||
FCommandBuffer CmdLine;
|
||||
|
||||
FCommandBuffer::FCommandBuffer(const FCommandBuffer &o)
|
||||
{
|
||||
Text = o.Text;
|
||||
CursorPos = o.CursorPos;
|
||||
StartPos = o.StartPos;
|
||||
}
|
||||
|
||||
FString FCommandBuffer::GetText() const
|
||||
{
|
||||
FString build;
|
||||
for (auto chr : Text) build.AppendCharacter(chr);
|
||||
return build;
|
||||
}
|
||||
|
||||
void FCommandBuffer::Draw(int x, int y, int scale, bool cursor)
|
||||
{
|
||||
if (scale == 1)
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c', TAG_DONE);
|
||||
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
|
||||
&Text[StartPos], TAG_DONE);
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
|
||||
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
|
||||
y, '\xb', TAG_DONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c',
|
||||
DTA_VirtualWidth, twod->GetWidth() / scale,
|
||||
DTA_VirtualHeight, twod->GetHeight() / scale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
|
||||
&Text[StartPos],
|
||||
DTA_VirtualWidth, twod->GetWidth() / scale,
|
||||
DTA_VirtualHeight, twod->GetHeight() / scale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
|
||||
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
|
||||
y, '\xb',
|
||||
DTA_VirtualWidth, twod->GetWidth() / scale,
|
||||
DTA_VirtualHeight, twod->GetHeight() / scale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned FCommandBuffer::CalcCellSize(unsigned length)
|
||||
{
|
||||
unsigned cellcount = 0;
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
int w;
|
||||
NewConsoleFont->GetChar(Text[i], CR_UNTRANSLATED, &w);
|
||||
cellcount += w / 9;
|
||||
}
|
||||
return cellcount;
|
||||
|
||||
}
|
||||
|
||||
unsigned FCommandBuffer::CharsForCells(unsigned cellin, bool *overflow)
|
||||
{
|
||||
unsigned chars = 0;
|
||||
int cells = cellin;
|
||||
while (cells > 0)
|
||||
{
|
||||
int w;
|
||||
NewConsoleFont->GetChar(Text[chars++], CR_UNTRANSLATED, &w);
|
||||
cells -= w / 9;
|
||||
}
|
||||
*overflow = (cells < 0);
|
||||
return chars;
|
||||
}
|
||||
|
||||
|
||||
void FCommandBuffer::MakeStartPosGood()
|
||||
{
|
||||
// Make sure both values point to something valid.
|
||||
if (CursorPos > Text.length()) CursorPos = (unsigned)Text.length();
|
||||
if (StartPos > Text.length()) StartPos = (unsigned)Text.length();
|
||||
|
||||
CursorPosCells = CalcCellSize(CursorPos);
|
||||
StartPosCells = CalcCellSize(StartPos);
|
||||
unsigned LengthCells = CalcCellSize((unsigned)Text.length());
|
||||
|
||||
int n = StartPosCells;
|
||||
unsigned cols = ConCols / active_con_scale(twod);
|
||||
|
||||
if (StartPosCells >= LengthCells)
|
||||
{ // Start of visible line is beyond end of line
|
||||
n = CursorPosCells - cols + 2;
|
||||
}
|
||||
if ((CursorPosCells - StartPosCells) >= cols - 2)
|
||||
{ // The cursor is beyond the visible part of the line
|
||||
n = CursorPosCells - cols + 2;
|
||||
}
|
||||
if (StartPosCells > CursorPosCells)
|
||||
{ // The cursor is in front of the visible part of the line
|
||||
n = CursorPosCells;
|
||||
}
|
||||
StartPosCells = std::max(0, n);
|
||||
bool overflow;
|
||||
StartPos = CharsForCells(StartPosCells, &overflow);
|
||||
if (overflow)
|
||||
{
|
||||
// We ended up in the middle of a double cell character, so set the start to the following character.
|
||||
StartPosCells++;
|
||||
StartPos = CharsForCells(StartPosCells, &overflow);
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::CursorStart()
|
||||
{
|
||||
CursorPos = 0;
|
||||
StartPos = 0;
|
||||
CursorPosCells = 0;
|
||||
StartPosCells = 0;
|
||||
}
|
||||
|
||||
void FCommandBuffer::CursorEnd()
|
||||
{
|
||||
CursorPos = (unsigned)Text.length();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
|
||||
void FCommandBuffer::CursorLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
MoveCursorLeft();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::CursorRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
MoveCursorRight();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::CursorWordLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
do MoveCursorLeft();
|
||||
while (CursorPos > 0 && Text[CursorPos - 1] != ' ');
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::CursorWordRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
do MoveCursorRight();
|
||||
while (CursorPos < Text.length() && Text[CursorPos] != ' ');
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::DeleteLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
MoveCursorLeft();
|
||||
Text.erase(CursorPos, 1);
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::DeleteRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
Text.erase(CursorPos, 1);
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::DeleteWordLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
auto now = CursorPos;
|
||||
|
||||
CursorWordLeft();
|
||||
|
||||
if (AppendToYankBuffer) {
|
||||
YankBuffer = Text.substr(CursorPos, now - CursorPos) + YankBuffer;
|
||||
} else {
|
||||
YankBuffer = Text.substr(CursorPos, now - CursorPos);
|
||||
}
|
||||
Text.erase(CursorPos, now - CursorPos);
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::DeleteLineLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
if (AppendToYankBuffer) {
|
||||
YankBuffer = Text.substr(0, CursorPos) + YankBuffer;
|
||||
} else {
|
||||
YankBuffer = Text.substr(0, CursorPos);
|
||||
}
|
||||
Text.erase(0, CursorPos);
|
||||
CursorStart();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::DeleteLineRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
if (AppendToYankBuffer) {
|
||||
YankBuffer += Text.substr(CursorPos, Text.length() - CursorPos);
|
||||
} else {
|
||||
YankBuffer = Text.substr(CursorPos, Text.length() - CursorPos);
|
||||
}
|
||||
Text.resize(CursorPos);
|
||||
CursorEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::AddChar(int character)
|
||||
{
|
||||
if (Text.length() == 0)
|
||||
{
|
||||
Text += character;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text.insert(CursorPos, 1, character);
|
||||
}
|
||||
CursorPos++;
|
||||
MakeStartPosGood();
|
||||
}
|
||||
|
||||
void FCommandBuffer::AddString(FString clip)
|
||||
{
|
||||
if (clip.IsNotEmpty())
|
||||
{
|
||||
// Only paste the first line.
|
||||
long brk = clip.IndexOfAny("\r\n\b");
|
||||
std::u32string build;
|
||||
if (brk >= 0)
|
||||
{
|
||||
clip.Truncate(brk);
|
||||
}
|
||||
auto strp = (const uint8_t*)clip.GetChars();
|
||||
while (auto chr = GetCharFromString(strp)) build += chr;
|
||||
|
||||
if (Text.length() == 0)
|
||||
{
|
||||
Text = build;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text.insert(CursorPos, build);
|
||||
}
|
||||
CursorPos += (unsigned)build.length();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void FCommandBuffer::SetString(const FString &str)
|
||||
{
|
||||
Text.clear();
|
||||
auto strp = (const uint8_t*)str.GetChars();
|
||||
while (auto chr = GetCharFromString(strp)) Text += chr;
|
||||
|
||||
CursorEnd();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
|
||||
void FCommandBuffer::AddYankBuffer()
|
||||
{
|
||||
if (YankBuffer.length() > 0)
|
||||
{
|
||||
if (Text.length() == 0)
|
||||
{
|
||||
Text = YankBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text.insert(CursorPos, YankBuffer);
|
||||
}
|
||||
CursorPos += (unsigned)YankBuffer.length();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
|
@ -1,38 +1,5 @@
|
|||
/*
|
||||
** c_commandbuffer.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** Copyright 2010-2020 Christoph Oelckers
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
// for inclusion in c_console.cpp.
|
||||
#include <string>
|
||||
#include "zstring.h"
|
||||
|
||||
struct FCommandBuffer
|
||||
{
|
||||
|
@ -51,142 +18,20 @@ public:
|
|||
|
||||
FCommandBuffer() = default;
|
||||
|
||||
FCommandBuffer(const FCommandBuffer &o)
|
||||
{
|
||||
Text = o.Text;
|
||||
CursorPos = o.CursorPos;
|
||||
StartPos = o.StartPos;
|
||||
}
|
||||
|
||||
FString GetText() const
|
||||
{
|
||||
FString build;
|
||||
for (auto chr : Text) build.AppendCharacter(chr);
|
||||
return build;
|
||||
}
|
||||
FCommandBuffer(const FCommandBuffer &o);
|
||||
FString GetText() const;
|
||||
|
||||
size_t TextLength() const
|
||||
{
|
||||
return Text.length();
|
||||
}
|
||||
|
||||
void Draw(int x, int y, int scale, bool cursor)
|
||||
{
|
||||
if (scale == 1)
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c', TAG_DONE);
|
||||
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
|
||||
&Text[StartPos], TAG_DONE);
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
|
||||
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
|
||||
y, '\xb', TAG_DONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c',
|
||||
DTA_VirtualWidth, twod->GetWidth() / scale,
|
||||
DTA_VirtualHeight, twod->GetHeight() / scale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
|
||||
&Text[StartPos],
|
||||
DTA_VirtualWidth, twod->GetWidth() / scale,
|
||||
DTA_VirtualHeight, twod->GetHeight() / scale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
|
||||
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
|
||||
y, '\xb',
|
||||
DTA_VirtualWidth, twod->GetWidth() / scale,
|
||||
DTA_VirtualHeight, twod->GetHeight() / scale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned CalcCellSize(unsigned length)
|
||||
{
|
||||
unsigned cellcount = 0;
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
int w;
|
||||
NewConsoleFont->GetChar(Text[i], CR_UNTRANSLATED, &w);
|
||||
cellcount += w / 9;
|
||||
}
|
||||
return cellcount;
|
||||
|
||||
}
|
||||
|
||||
unsigned CharsForCells(unsigned cellin, bool *overflow)
|
||||
{
|
||||
unsigned chars = 0;
|
||||
int cells = cellin;
|
||||
while (cells > 0)
|
||||
{
|
||||
int w;
|
||||
NewConsoleFont->GetChar(Text[chars++], CR_UNTRANSLATED, &w);
|
||||
cells -= w / 9;
|
||||
}
|
||||
*overflow = (cells < 0);
|
||||
return chars;
|
||||
}
|
||||
|
||||
|
||||
void MakeStartPosGood()
|
||||
{
|
||||
// Make sure both values point to something valid.
|
||||
if (CursorPos > Text.length()) CursorPos = (unsigned)Text.length();
|
||||
if (StartPos > Text.length()) StartPos = (unsigned)Text.length();
|
||||
|
||||
CursorPosCells = CalcCellSize(CursorPos);
|
||||
StartPosCells = CalcCellSize(StartPos);
|
||||
unsigned LengthCells = CalcCellSize((unsigned)Text.length());
|
||||
|
||||
int n = StartPosCells;
|
||||
unsigned cols = ConCols / active_con_scale(twod);
|
||||
|
||||
if (StartPosCells >= LengthCells)
|
||||
{ // Start of visible line is beyond end of line
|
||||
n = CursorPosCells - cols + 2;
|
||||
}
|
||||
if ((CursorPosCells - StartPosCells) >= cols - 2)
|
||||
{ // The cursor is beyond the visible part of the line
|
||||
n = CursorPosCells - cols + 2;
|
||||
}
|
||||
if (StartPosCells > CursorPosCells)
|
||||
{ // The cursor is in front of the visible part of the line
|
||||
n = CursorPosCells;
|
||||
}
|
||||
StartPosCells = std::max(0, n);
|
||||
bool overflow;
|
||||
StartPos = CharsForCells(StartPosCells, &overflow);
|
||||
if (overflow)
|
||||
{
|
||||
// We ended up in the middle of a double cell character, so set the start to the following character.
|
||||
StartPosCells++;
|
||||
StartPos = CharsForCells(StartPosCells, &overflow);
|
||||
}
|
||||
}
|
||||
|
||||
void CursorStart()
|
||||
{
|
||||
CursorPos = 0;
|
||||
StartPos = 0;
|
||||
CursorPosCells = 0;
|
||||
StartPosCells = 0;
|
||||
}
|
||||
|
||||
void CursorEnd()
|
||||
{
|
||||
CursorPos = (unsigned)Text.length();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
void Draw(int x, int y, int scale, bool cursor);
|
||||
unsigned CalcCellSize(unsigned length);
|
||||
unsigned CharsForCells(unsigned cellin, bool *overflow);
|
||||
void MakeStartPosGood();;
|
||||
void CursorStart();
|
||||
void CursorEnd();
|
||||
|
||||
private:
|
||||
void MoveCursorLeft()
|
||||
|
@ -200,174 +45,19 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
void CursorLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
MoveCursorLeft();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void CursorRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
MoveCursorRight();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void CursorWordLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
do MoveCursorLeft();
|
||||
while (CursorPos > 0 && Text[CursorPos - 1] != ' ');
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void CursorWordRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
do MoveCursorRight();
|
||||
while (CursorPos < Text.length() && Text[CursorPos] != ' ');
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
MoveCursorLeft();
|
||||
Text.erase(CursorPos, 1);
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
Text.erase(CursorPos, 1);
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteWordLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
auto now = CursorPos;
|
||||
|
||||
CursorWordLeft();
|
||||
|
||||
if (AppendToYankBuffer) {
|
||||
YankBuffer = Text.substr(CursorPos, now - CursorPos) + YankBuffer;
|
||||
} else {
|
||||
YankBuffer = Text.substr(CursorPos, now - CursorPos);
|
||||
}
|
||||
Text.erase(CursorPos, now - CursorPos);
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteLineLeft()
|
||||
{
|
||||
if (CursorPos > 0)
|
||||
{
|
||||
if (AppendToYankBuffer) {
|
||||
YankBuffer = Text.substr(0, CursorPos) + YankBuffer;
|
||||
} else {
|
||||
YankBuffer = Text.substr(0, CursorPos);
|
||||
}
|
||||
Text.erase(0, CursorPos);
|
||||
CursorStart();
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteLineRight()
|
||||
{
|
||||
if (CursorPos < Text.length())
|
||||
{
|
||||
if (AppendToYankBuffer) {
|
||||
YankBuffer += Text.substr(CursorPos, Text.length() - CursorPos);
|
||||
} else {
|
||||
YankBuffer = Text.substr(CursorPos, Text.length() - CursorPos);
|
||||
}
|
||||
Text.resize(CursorPos);
|
||||
CursorEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void AddChar(int character)
|
||||
{
|
||||
if (Text.length() == 0)
|
||||
{
|
||||
Text += character;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text.insert(CursorPos, 1, character);
|
||||
}
|
||||
CursorPos++;
|
||||
MakeStartPosGood();
|
||||
}
|
||||
|
||||
void AddString(FString clip)
|
||||
{
|
||||
if (clip.IsNotEmpty())
|
||||
{
|
||||
// Only paste the first line.
|
||||
long brk = clip.IndexOfAny("\r\n\b");
|
||||
std::u32string build;
|
||||
if (brk >= 0)
|
||||
{
|
||||
clip.Truncate(brk);
|
||||
}
|
||||
auto strp = (const uint8_t*)clip.GetChars();
|
||||
while (auto chr = GetCharFromString(strp)) build += chr;
|
||||
|
||||
if (Text.length() == 0)
|
||||
{
|
||||
Text = build;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text.insert(CursorPos, build);
|
||||
}
|
||||
CursorPos += (unsigned)build.length();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
|
||||
void SetString(const FString &str)
|
||||
{
|
||||
Text.clear();
|
||||
auto strp = (const uint8_t*)str.GetChars();
|
||||
while (auto chr = GetCharFromString(strp)) Text += chr;
|
||||
|
||||
CursorEnd();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
|
||||
void AddYankBuffer()
|
||||
{
|
||||
if (YankBuffer.length() > 0)
|
||||
{
|
||||
if (Text.length() == 0)
|
||||
{
|
||||
Text = YankBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text.insert(CursorPos, YankBuffer);
|
||||
}
|
||||
CursorPos += (unsigned)YankBuffer.length();
|
||||
MakeStartPosGood();
|
||||
}
|
||||
}
|
||||
void CursorLeft();
|
||||
void CursorRight();
|
||||
void CursorWordLeft();
|
||||
void CursorWordRight();
|
||||
void DeleteLeft();
|
||||
void DeleteRight();
|
||||
void DeleteWordLeft();
|
||||
void DeleteLineLeft();
|
||||
void DeleteLineRight();
|
||||
void AddChar(int character);
|
||||
void AddString(FString clip);
|
||||
void SetString(const FString &str);
|
||||
void AddYankBuffer();
|
||||
};
|
||||
|
||||
extern FCommandBuffer CmdLine;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include "basics.h"
|
||||
#include "c_tabcomplete.h"
|
||||
|
||||
struct event_t;
|
||||
|
||||
|
@ -81,9 +82,6 @@ void C_MidPrint (FFont *font, const char *message, bool bold = false);
|
|||
|
||||
bool C_Responder (event_t *ev);
|
||||
|
||||
void C_AddTabCommand (const char *name);
|
||||
void C_RemoveTabCommand (const char *name);
|
||||
void C_ClearTabCommands(); // Removes all tab commands
|
||||
void C_SetNotifyFontScale(double scale);
|
||||
|
||||
extern const char *console_bar;
|
||||
|
|
322
source/common/console/c_tabcomplete.cpp
Normal file
322
source/common/console/c_tabcomplete.cpp
Normal file
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
** 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;
|
||||
}
|
9
source/common/console/c_tabcomplete.h
Normal file
9
source/common/console/c_tabcomplete.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
void C_AddTabCommand (const char *name);
|
||||
void C_RemoveTabCommand (const char *name);
|
||||
void C_ClearTabCommands(); // Removes all tab commands
|
||||
void C_TabComplete(bool goForward);
|
||||
bool C_TabCompleteList();
|
||||
extern bool TabbedLast; // True if last key pressed was tab
|
||||
extern bool TabbedList; // True if tab list was shown
|
|
@ -88,13 +88,6 @@ CUSTOM_CVAR(Int, con_buffersize, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
|||
|
||||
FConsoleBuffer *conbuffer;
|
||||
|
||||
static void C_TabComplete (bool goForward);
|
||||
static bool C_TabCompleteList ();
|
||||
static bool TabbedLast; // True if last key pressed was tab
|
||||
static bool TabbedList; // True if tab list was shown
|
||||
CVAR(Bool, con_notablist, false, CVAR_ARCHIVE)
|
||||
|
||||
|
||||
static FTextureID conback;
|
||||
static uint32_t conshade;
|
||||
static bool conline;
|
||||
|
@ -179,8 +172,6 @@ struct History
|
|||
FString String;
|
||||
};
|
||||
|
||||
static FCommandBuffer CmdLine;
|
||||
|
||||
#define MAXHISTSIZE 50
|
||||
static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL;
|
||||
static int HistSize;
|
||||
|
@ -1546,277 +1537,3 @@ void C_MidPrint (FFont *font, const char *msg, bool bold)
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
/****** Tab completion code ******/
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static 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();
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue