You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
915 lines
33 KiB
C++
915 lines
33 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include "LUAEditorSyntaxHighlighter.hxx"
|
|
#include <Source/LUA/moc_LUAEditorSyntaxHighlighter.cpp>
|
|
#include "LUAEditorStyleMessages.h"
|
|
#include "LUAEditorBlockState.h"
|
|
|
|
namespace LUAEditor
|
|
{
|
|
namespace
|
|
{
|
|
template<typename Container>
|
|
void CreateTypes([[maybe_unused]] Container& container)
|
|
{
|
|
}
|
|
|
|
template<typename Container, typename Type, typename ... Types>
|
|
void CreateTypes(Container& container)
|
|
{
|
|
container.push_back(azcreate(Type, ()));
|
|
CreateTypes<Container, Types...>(container);
|
|
}
|
|
|
|
enum class ParserStates : int
|
|
{
|
|
Null,
|
|
Name,
|
|
ShortComment,
|
|
LongComment,
|
|
Number,
|
|
NumberHex,
|
|
StringLiteral,
|
|
NumStates
|
|
};
|
|
|
|
class BaseParserState
|
|
{
|
|
public:
|
|
virtual ~BaseParserState() {}
|
|
virtual bool IsMultilineState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine) const { return false; }
|
|
virtual void StartState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine) {}
|
|
//note you only get 13 bits of usable space here. see QTBlockState m_syntaxHighlighterStateExtra
|
|
virtual AZ::u16 GetSaveState() const { return 0; }
|
|
virtual void SetSaveState([[maybe_unused]] AZ::u16 state) {}
|
|
virtual void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) = 0;
|
|
};
|
|
|
|
class NullParserState
|
|
: public BaseParserState
|
|
{
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
};
|
|
|
|
class NameParserState
|
|
: public BaseParserState
|
|
{
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
};
|
|
|
|
class ShortCommentParserState
|
|
: public BaseParserState
|
|
{
|
|
void StartState(LUASyntaxHighlighter::StateMachine& machine) override;
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
bool m_mightBeLong;
|
|
};
|
|
|
|
class NumberParserState
|
|
: public BaseParserState
|
|
{
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
};
|
|
|
|
class NumberHexParserState
|
|
: public BaseParserState
|
|
{
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
};
|
|
|
|
class LongCommentParserState
|
|
: public BaseParserState
|
|
{
|
|
bool IsMultilineState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine) const override { return true; }
|
|
void StartState(LUASyntaxHighlighter::StateMachine& machine) override;
|
|
AZ::u16 GetSaveState() const override { return m_bracketLevel; }
|
|
void SetSaveState(AZ::u16 state) override;
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
AZ::u16 m_bracketLevel;
|
|
QString m_bracketEnd;
|
|
bool m_endNextChar;
|
|
};
|
|
|
|
class StringLiteralParserState
|
|
: public BaseParserState
|
|
{
|
|
bool IsMultilineState(LUASyntaxHighlighter::StateMachine& machine) const override;
|
|
void StartState(LUASyntaxHighlighter::StateMachine& machine) override;
|
|
AZ::u16 GetSaveState() const override { return m_bracketLevel; }
|
|
void SetSaveState(AZ::u16 state) override;
|
|
void Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar) override;
|
|
AZ::u16 m_bracketLevel; //if 0 string started with a ' if 1 started with a " if 2 or more started with an long braket of level m_BracketLevel-2
|
|
bool m_endNextChar;
|
|
QString m_bracketEnd;
|
|
bool m_mightBeLong;
|
|
};
|
|
}
|
|
|
|
class LUASyntaxHighlighter::StateMachine
|
|
{
|
|
public:
|
|
StateMachine();
|
|
~StateMachine();
|
|
void Parse(const QString& text);
|
|
//extraBack is if you want to include previous chars as part as the current string after the state change
|
|
void SetState(ParserStates state, int extraBack = 0);
|
|
void PassState(ParserStates state); //change state but keep data captured so far, use if we are in "wrong" state
|
|
void Reset();
|
|
AZStd::function<void(ParserStates state, int position, int length)> CaptureToken;
|
|
int CurrentLength() const { return m_currentChar - m_start + 1; }
|
|
QStringRef CurrentString() const { return QStringRef(m_currentString, m_start, CurrentLength()); }
|
|
const QString* GetFullLine() const { return m_currentString; }
|
|
|
|
QTBlockState GetSaveState() const;
|
|
void SetSaveState(QTBlockState state);
|
|
|
|
void IncFoldLevel()
|
|
{
|
|
++m_foldLevel;
|
|
if (m_onIncFoldLevel)
|
|
{
|
|
m_onIncFoldLevel(m_foldLevel);
|
|
}
|
|
}
|
|
void DecFoldLevel()
|
|
{
|
|
if (m_foldLevel > 0)
|
|
{
|
|
--m_foldLevel;
|
|
}
|
|
if (m_onDecFoldLevel)
|
|
{
|
|
m_onDecFoldLevel(m_foldLevel);
|
|
}
|
|
}
|
|
|
|
ParserStates GetCurrentParserState() const { return m_currentState; }
|
|
|
|
bool IsJoiningNames() const { return m_joinNames; }
|
|
void SetJoiningNames(bool joinNames) { m_joinNames = joinNames; }
|
|
|
|
template<typename T>
|
|
void SetOnIncFoldLevel(const T& callable) { m_onIncFoldLevel = callable; }
|
|
template<typename T>
|
|
void SetOnDecFoldLevel(const T& callable) { m_onDecFoldLevel = callable; }
|
|
|
|
private:
|
|
BaseParserState* GetCurrentState() const { return m_states[static_cast<int>(m_currentState)]; }
|
|
|
|
typedef AZStd::fixed_vector<BaseParserState*, static_cast<int>(ParserStates::NumStates)> StatesListType;
|
|
StatesListType m_states;
|
|
ParserStates m_currentState {
|
|
ParserStates::Null
|
|
};
|
|
int m_currentChar;
|
|
int m_start;
|
|
const QString* m_currentString;
|
|
int m_foldLevel;
|
|
|
|
bool m_joinNames {
|
|
true
|
|
}; //consider names seperated by . and : as one for highlighting purposes
|
|
|
|
AZStd::function<void(int)> m_onIncFoldLevel;
|
|
AZStd::function<void(int)> m_onDecFoldLevel;
|
|
};
|
|
|
|
LUASyntaxHighlighter::StateMachine::StateMachine()
|
|
{
|
|
CreateTypes<StatesListType, NullParserState, NameParserState, ShortCommentParserState,
|
|
LongCommentParserState, NumberParserState, NumberHexParserState, StringLiteralParserState>(m_states);
|
|
Reset();
|
|
}
|
|
|
|
LUASyntaxHighlighter::StateMachine::~StateMachine()
|
|
{
|
|
AZStd::for_each(m_states.begin(), m_states.end(), [](BaseParserState* state) { azdestroy(state); });
|
|
}
|
|
|
|
void LUASyntaxHighlighter::StateMachine::Reset()
|
|
{
|
|
m_start = 0;
|
|
m_currentChar = -1;
|
|
m_currentState = ParserStates::Null;
|
|
m_currentString = nullptr;
|
|
m_foldLevel = 0;
|
|
}
|
|
|
|
void LUASyntaxHighlighter::StateMachine::Parse(const QString& text)
|
|
{
|
|
m_currentString = &text;
|
|
for (m_currentChar = 0; m_currentChar != text.length(); ++m_currentChar)
|
|
{
|
|
GetCurrentState()->Parse(*this, text[m_currentChar]);
|
|
}
|
|
//we only highlight on line at most at a time. so if this is a multiline highlight, go ahead an highlight this line as part of that now.
|
|
if (GetCurrentState()->IsMultilineState(*this))
|
|
{
|
|
if (CaptureToken && m_start != m_currentChar)
|
|
{
|
|
CaptureToken(m_currentState, m_start, m_currentChar - m_start);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_currentState != ParserStates::Null)
|
|
{
|
|
SetState(ParserStates::Null);
|
|
}
|
|
else
|
|
{
|
|
if (CaptureToken && m_start != m_currentChar)
|
|
{
|
|
CaptureToken(m_currentState, m_start, m_currentChar - m_start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LUASyntaxHighlighter::StateMachine::SetState(ParserStates state, int extraBack)
|
|
{
|
|
if (m_currentState != state)
|
|
{
|
|
int currentChar = m_currentChar - extraBack;
|
|
|
|
if (CaptureToken && m_start < currentChar)
|
|
{
|
|
CaptureToken(m_currentState, m_start, currentChar - m_start);
|
|
}
|
|
m_currentState = state;
|
|
m_start = currentChar;
|
|
GetCurrentState()->StartState(*this);
|
|
//if going back to null state, see if this char might be the start of a new capture.
|
|
if (m_currentState == ParserStates::Null && m_start < m_currentString->length())
|
|
{
|
|
GetCurrentState()->Parse(*this, m_currentString->at(m_start));
|
|
}
|
|
}
|
|
}
|
|
|
|
void LUASyntaxHighlighter::StateMachine::PassState(ParserStates state)
|
|
{
|
|
m_currentState = state;
|
|
GetCurrentState()->StartState(*this);
|
|
//if going back to null state, see if this char might be the start of a new capture.
|
|
if (m_currentState == ParserStates::Null && m_start < m_currentString->length())
|
|
{
|
|
GetCurrentState()->Parse(*this, m_currentString->at(m_currentChar));
|
|
}
|
|
}
|
|
|
|
///1st bit to detect uninitialized blocks, next 14 bits store folding depth, next 3 bits store state machine state, final 14 bits store state specific user data.
|
|
QTBlockState LUASyntaxHighlighter::StateMachine::GetSaveState() const
|
|
{
|
|
QTBlockState result;
|
|
result.m_blockState.m_uninitialized = 0;
|
|
result.m_blockState.m_folded = 0;
|
|
result.m_blockState.m_foldLevel = m_foldLevel;
|
|
result.m_blockState.m_syntaxHighlighterState = static_cast<int>(m_currentState);
|
|
result.m_blockState.m_syntaxHighlighterStateExtra = GetCurrentState()->GetSaveState();
|
|
return result;
|
|
}
|
|
|
|
void LUASyntaxHighlighter::StateMachine::SetSaveState(QTBlockState state)
|
|
{
|
|
static_assert(static_cast<int>(ParserStates::NumStates) <= 8, "We are only using 3 bits for state in lua parser currently");
|
|
|
|
Reset();
|
|
if (!state.m_blockState.m_uninitialized)
|
|
{
|
|
m_currentState = static_cast<ParserStates>(state.m_blockState.m_syntaxHighlighterState);
|
|
GetCurrentState()->SetSaveState(state.m_blockState.m_syntaxHighlighterStateExtra);
|
|
m_foldLevel = state.m_blockState.m_foldLevel;
|
|
}
|
|
}
|
|
|
|
void NullParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
|
|
{
|
|
if (nextChar.isLetter() || nextChar == '_')
|
|
{
|
|
machine.SetState(ParserStates::Name);
|
|
}
|
|
else if (nextChar.isNumber() || nextChar == '-' || nextChar == '+')
|
|
{
|
|
machine.SetState(ParserStates::Number);
|
|
}
|
|
else if (nextChar == '\'' || nextChar == '"' || nextChar == '[')
|
|
{
|
|
machine.SetState(ParserStates::StringLiteral);
|
|
}
|
|
else if (nextChar == '{')
|
|
{
|
|
machine.IncFoldLevel();
|
|
}
|
|
else if (nextChar == '}')
|
|
{
|
|
machine.DecFoldLevel();
|
|
}
|
|
}
|
|
|
|
void NameParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
|
|
{
|
|
if (!nextChar.isLetterOrNumber() && nextChar != '_' && (!machine.IsJoiningNames() || (nextChar != '.' && nextChar != ':')))
|
|
{
|
|
machine.SetState(ParserStates::Null);
|
|
}
|
|
}
|
|
|
|
void ShortCommentParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
|
|
{
|
|
if (machine.CurrentLength() == 3 && nextChar != '[')
|
|
{
|
|
m_mightBeLong = false;
|
|
}
|
|
else if (machine.CurrentLength() >= 4 && machine.CurrentLength() < USHRT_MAX && m_mightBeLong) //we cant catch longer than 16k level comments.
|
|
{
|
|
if (nextChar != '=' && nextChar != '[')
|
|
{
|
|
m_mightBeLong = false;
|
|
}
|
|
else if (nextChar == '[')
|
|
{
|
|
machine.PassState(ParserStates::LongComment);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShortCommentParserState::StartState([[maybe_unused]] LUASyntaxHighlighter::StateMachine& machine)
|
|
{
|
|
m_mightBeLong = true;
|
|
}
|
|
|
|
void LongCommentParserState::StartState(LUASyntaxHighlighter::StateMachine& machine)
|
|
{
|
|
auto token = machine.CurrentString();
|
|
int start = token.indexOf('[');
|
|
AZ_Assert(start != -1, "Shouldn't have been able to get to this state without opening long bracket");
|
|
int finish = token.indexOf('[', start + 1);
|
|
AZ_Assert(finish != -1 && finish != start, "Shouldn't have been able to get to this state without opening long bracket");
|
|
m_bracketLevel = static_cast<AZ::u16>(finish - start - 1);
|
|
|
|
m_bracketEnd = "]";
|
|
for (int i = 0; i < m_bracketLevel; ++i)
|
|
{
|
|
m_bracketEnd.append('=');
|
|
}
|
|
m_bracketEnd.append(']');
|
|
m_endNextChar = false;
|
|
}
|
|
|
|
void LongCommentParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, [[maybe_unused]] const QChar& nextChar)
|
|
{
|
|
if (m_endNextChar)
|
|
{
|
|
machine.SetState(ParserStates::Null);
|
|
return;
|
|
}
|
|
|
|
QStringRef token = machine.CurrentString();
|
|
if (token.endsWith(m_bracketEnd))
|
|
{
|
|
if (machine.GetFullLine()->size() >= token.size())
|
|
{
|
|
machine.SetState(ParserStates::Null, -1);
|
|
}
|
|
else
|
|
{
|
|
m_endNextChar = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LongCommentParserState::SetSaveState(AZ::u16 state)
|
|
{
|
|
m_bracketLevel = state;
|
|
m_bracketEnd = "]";
|
|
for (int i = 0; i < m_bracketLevel; ++i)
|
|
{
|
|
m_bracketEnd.append('=');
|
|
}
|
|
m_bracketEnd.append(']');
|
|
m_endNextChar = false;
|
|
}
|
|
|
|
void NumberParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
|
|
{
|
|
auto currentString = machine.CurrentString();
|
|
if (currentString.endsWith("--"))
|
|
{
|
|
machine.SetState(ParserStates::ShortComment, 1);
|
|
return;
|
|
}
|
|
|
|
auto lower = nextChar.toLower();
|
|
|
|
if (lower == 'x')
|
|
{
|
|
machine.PassState(ParserStates::NumberHex);
|
|
}
|
|
else if (!(nextChar.isNumber() || nextChar == '.' || lower == 'e'))
|
|
{
|
|
if (currentString.length() == 2 && (currentString.at(0) == '+' || currentString.at(0) == '-'))
|
|
{
|
|
machine.PassState(ParserStates::Null);
|
|
}
|
|
else
|
|
{
|
|
machine.SetState(ParserStates::Null);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NumberHexParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
|
|
{
|
|
auto lower = nextChar.toLower();
|
|
|
|
if (!(nextChar.isNumber() || nextChar == '.' || lower == 'p'))
|
|
{
|
|
machine.SetState(ParserStates::Null);
|
|
}
|
|
}
|
|
|
|
void StringLiteralParserState::StartState(LUASyntaxHighlighter::StateMachine& machine)
|
|
{
|
|
m_endNextChar = false;
|
|
m_mightBeLong = false;
|
|
auto currentString = machine.CurrentString();
|
|
AZ_Assert(!currentString.isEmpty(), "Sting literal string shouldn't be empty")
|
|
if (currentString.at(0) == '\'')
|
|
{
|
|
m_bracketLevel = 0;
|
|
}
|
|
if (currentString.at(0) == '"')
|
|
{
|
|
m_bracketLevel = 1;
|
|
}
|
|
if (currentString.at(0) == '[')
|
|
{
|
|
m_bracketLevel = 2;
|
|
m_mightBeLong = true;
|
|
m_bracketEnd = "]";
|
|
}
|
|
}
|
|
|
|
void StringLiteralParserState::Parse(LUASyntaxHighlighter::StateMachine& machine, const QChar& nextChar)
|
|
{
|
|
if (m_endNextChar)
|
|
{
|
|
machine.SetState(ParserStates::Null);
|
|
return;
|
|
}
|
|
|
|
if (m_mightBeLong)
|
|
{
|
|
if (nextChar == '[')
|
|
{
|
|
m_mightBeLong = false; //it actually is long, = length will already be in m_bracketLevel
|
|
m_bracketEnd += ']';
|
|
return;
|
|
}
|
|
else if (nextChar == '=')
|
|
{
|
|
++m_bracketLevel;
|
|
m_bracketEnd += '=';
|
|
}
|
|
else
|
|
{
|
|
machine.PassState(ParserStates::Null); //turns out we weren't actually a string literal
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_bracketLevel == 0 && nextChar == '\'' && !machine.CurrentString().endsWith("\\'"))
|
|
{
|
|
m_endNextChar = true;
|
|
}
|
|
if (m_bracketLevel == 1 && nextChar == '"' && !machine.CurrentString().endsWith("\\\""))
|
|
{
|
|
m_endNextChar = true;
|
|
}
|
|
if (m_bracketLevel >= 2 && machine.CurrentString().endsWith(m_bracketEnd))
|
|
{
|
|
m_endNextChar = true;
|
|
}
|
|
}
|
|
|
|
bool StringLiteralParserState::IsMultilineState(LUASyntaxHighlighter::StateMachine& machine) const
|
|
{
|
|
auto currentString = machine.GetFullLine();
|
|
|
|
if ((m_bracketLevel > 1 && !m_endNextChar && !m_mightBeLong) || currentString->endsWith(QString("\\")) || currentString->endsWith(QString("\\z")))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void StringLiteralParserState::SetSaveState(AZ::u16 state)
|
|
{
|
|
m_bracketLevel = state;
|
|
if (m_bracketLevel >= 2)
|
|
{
|
|
m_bracketEnd = "]";
|
|
for (int i = 0; i < m_bracketLevel - 2; ++i)
|
|
{
|
|
m_bracketEnd.append('=');
|
|
}
|
|
m_bracketEnd.append(']');
|
|
}
|
|
m_endNextChar = false;
|
|
m_mightBeLong = false;
|
|
}
|
|
|
|
LUASyntaxHighlighter::LUASyntaxHighlighter(QWidget* parent)
|
|
: QSyntaxHighlighter(parent)
|
|
, m_machine(azcreate(StateMachine, ()))
|
|
{
|
|
AddBlockKeywords();
|
|
}
|
|
|
|
LUASyntaxHighlighter::LUASyntaxHighlighter(QTextDocument* parent)
|
|
: QSyntaxHighlighter(parent)
|
|
, m_machine(azcreate(StateMachine, ()))
|
|
{
|
|
AddBlockKeywords();
|
|
}
|
|
|
|
void LUASyntaxHighlighter::AddBlockKeywords()
|
|
{
|
|
//these don't catch tables. that is handled in the null machine state.
|
|
m_LUAStartBlockKeywords.clear();
|
|
AZStd::string startKeywords[] = {
|
|
{"do"}, {"if"}, {"function"}, {"repeat"}
|
|
};
|
|
m_LUAStartBlockKeywords.insert(std::begin(startKeywords), std::end(startKeywords));
|
|
|
|
m_LUAEndBlockKeywords.clear();
|
|
AZStd::string endKeywords[] = {
|
|
{"end"}, {"until"}
|
|
};
|
|
m_LUAEndBlockKeywords.insert(std::begin(endKeywords), std::end(endKeywords));
|
|
}
|
|
|
|
LUASyntaxHighlighter::~LUASyntaxHighlighter()
|
|
{
|
|
azdestroy(m_machine);
|
|
}
|
|
|
|
void LUASyntaxHighlighter::highlightBlock(const QString& text)
|
|
{
|
|
m_machine->SetJoiningNames(true);
|
|
m_machine->SetOnIncFoldLevel([](int) {});
|
|
m_machine->SetOnDecFoldLevel([](int) {});
|
|
|
|
auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC("LUA Editor Text Settings", 0xb6e15565), AZ::UserSettings::CT_GLOBAL);
|
|
|
|
const HighlightedWords::LUAKeywordsType* keywords = nullptr;
|
|
EBUS_EVENT_RESULT(keywords, HighlightedWords::Bus, GetLUAKeywords);
|
|
const HighlightedWords::LUAKeywordsType* libraryFuncs = nullptr;
|
|
EBUS_EVENT_RESULT(libraryFuncs, HighlightedWords::Bus, GetLUALibraryFunctions);
|
|
|
|
auto cBlock = currentBlock();
|
|
|
|
QTBlockState currentState;
|
|
currentState.m_qtBlockState = currentBlockState();
|
|
QTextCharFormat textFormat;
|
|
textFormat.setFont(m_font);
|
|
|
|
textFormat.setForeground(colors->GetTextColor());
|
|
setFormat(0, cBlock.length(), textFormat);
|
|
|
|
QTextCharFormat spaceFormat = QTextCharFormat();
|
|
spaceFormat.setForeground(colors->GetTextWhitespaceColor());
|
|
|
|
QRegExp tabsAndSpaces("( |\t)+");
|
|
int index = tabsAndSpaces.indexIn(text);
|
|
while (index >= 0)
|
|
{
|
|
int length = tabsAndSpaces.matchedLength();
|
|
setFormat(index, length, spaceFormat);
|
|
index = tabsAndSpaces.indexIn(text, index + length);
|
|
}
|
|
|
|
//first take care of bracket highlighting. let later overwrite to handle case of brackets inside of a comment,ect
|
|
if (m_openBracketPos >= 0 && m_closeBracketPos >= 0)
|
|
{
|
|
if (cBlock.contains(m_openBracketPos))
|
|
{
|
|
setFormat(m_openBracketPos - cBlock.position(), 1, colors->GetBracketColor());
|
|
}
|
|
if (cBlock.contains(m_closeBracketPos))
|
|
{
|
|
setFormat(m_closeBracketPos - cBlock.position(), 1, colors->GetBracketColor());
|
|
}
|
|
}
|
|
else if (m_openBracketPos >= 0)
|
|
{
|
|
if (cBlock.contains(m_openBracketPos))
|
|
{
|
|
setFormat(m_openBracketPos - cBlock.position(), 1, colors->GetUnmatchedBracketColor());
|
|
}
|
|
if (cBlock.contains(m_closeBracketPos))
|
|
{
|
|
setFormat(m_closeBracketPos - cBlock.position(), 1, colors->GetUnmatchedBracketColor());
|
|
}
|
|
}
|
|
|
|
QTBlockState prevState;
|
|
prevState.m_qtBlockState = previousBlockState();
|
|
m_machine->SetSaveState(prevState);
|
|
auto startingState = m_machine->GetCurrentParserState();
|
|
|
|
m_machine->CaptureToken = [&](ParserStates state, int position, int length)
|
|
{
|
|
if (state == ParserStates::Name)
|
|
{
|
|
AZStd::string dhText(text.mid(position, length).toUtf8().constData());
|
|
if (keywords && keywords->find(dhText) != keywords->end())
|
|
{
|
|
textFormat.setForeground(colors->GetKeywordColor());
|
|
setFormat(position, length, textFormat);
|
|
}
|
|
else if (libraryFuncs && libraryFuncs->find(dhText) != libraryFuncs->end())
|
|
{
|
|
textFormat.setForeground(colors->GetLibraryColor());
|
|
setFormat(position, length, textFormat);
|
|
}
|
|
else
|
|
{
|
|
textFormat.setForeground(colors->GetTextColor());
|
|
setFormat(position, length, textFormat);
|
|
}
|
|
|
|
|
|
if (m_LUAStartBlockKeywords.find(dhText) != m_LUAStartBlockKeywords.end())
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
}
|
|
if (m_LUAEndBlockKeywords.find(dhText) != m_LUAEndBlockKeywords.end())
|
|
{
|
|
m_machine->DecFoldLevel();
|
|
}
|
|
}
|
|
if (state == ParserStates::ShortComment || state == ParserStates::LongComment)
|
|
{
|
|
textFormat.setForeground(colors->GetCommentColor());
|
|
setFormat(position, length, textFormat);
|
|
}
|
|
if (state == ParserStates::Number || state == ParserStates::NumberHex)
|
|
{
|
|
textFormat.setForeground(colors->GetNumberColor());
|
|
setFormat(position, length, textFormat);
|
|
}
|
|
if (state == ParserStates::StringLiteral)
|
|
{
|
|
textFormat.setForeground(colors->GetStringLiteralColor());
|
|
setFormat(position, length, textFormat);
|
|
}
|
|
};
|
|
m_machine->Parse(text);
|
|
|
|
auto endingState = m_machine->GetCurrentParserState();
|
|
if (startingState != ParserStates::LongComment && endingState == ParserStates::LongComment)
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
}
|
|
if (startingState != ParserStates::StringLiteral && endingState == ParserStates::StringLiteral) //should only be true if a long string literal
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
}
|
|
if (startingState == ParserStates::LongComment && endingState != ParserStates::LongComment)
|
|
{
|
|
m_machine->DecFoldLevel();
|
|
}
|
|
if (startingState == ParserStates::StringLiteral && endingState != ParserStates::StringLiteral) //should only be true if a long string literal
|
|
{
|
|
m_machine->DecFoldLevel();
|
|
}
|
|
|
|
QTBlockState newState = m_machine->GetSaveState();
|
|
newState.m_blockState.m_folded = currentState.m_blockState.m_uninitialized ? 0 : currentState.m_blockState.m_folded;
|
|
setCurrentBlockState(newState.m_qtBlockState);
|
|
}
|
|
|
|
void LUASyntaxHighlighter::SetBracketHighlighting(int openBracketPos, int closeBracketPos)
|
|
{
|
|
auto oldOpenBracketPos = m_openBracketPos;
|
|
auto oldcloseBracketPos = m_closeBracketPos;
|
|
m_openBracketPos = openBracketPos;
|
|
m_closeBracketPos = closeBracketPos;
|
|
auto openBlock = document()->findBlock(m_openBracketPos);
|
|
auto closeBlock = document()->findBlock(m_closeBracketPos);
|
|
|
|
if (openBlock.isValid())
|
|
{
|
|
rehighlightBlock(openBlock);
|
|
}
|
|
if (closeBlock.isValid())
|
|
{
|
|
rehighlightBlock(closeBlock);
|
|
}
|
|
|
|
if (oldOpenBracketPos >= 0)
|
|
{
|
|
openBlock = document()->findBlock(oldOpenBracketPos);
|
|
if (openBlock.isValid())
|
|
{
|
|
rehighlightBlock(openBlock);
|
|
}
|
|
}
|
|
if (oldcloseBracketPos >= 0)
|
|
{
|
|
closeBlock = document()->findBlock(oldcloseBracketPos);
|
|
if (closeBlock.isValid())
|
|
{
|
|
rehighlightBlock(closeBlock);
|
|
}
|
|
}
|
|
}
|
|
|
|
//This code is also getting the list of lua names that are currently in scope
|
|
QList<QTextEdit::ExtraSelection> LUASyntaxHighlighter::HighlightMatchingNames(const QTextCursor& cursor, const QString&) const
|
|
{
|
|
m_machine->SetJoiningNames(false);
|
|
m_machine->SetOnIncFoldLevel([](int) {});
|
|
m_machine->SetOnDecFoldLevel([](int) {});
|
|
|
|
const HighlightedWords::LUAKeywordsType* keywords = nullptr;
|
|
EBUS_EVENT_RESULT(keywords, HighlightedWords::Bus, GetLUAKeywords);
|
|
const HighlightedWords::LUAKeywordsType* libraryFuncs = nullptr;
|
|
EBUS_EVENT_RESULT(libraryFuncs, HighlightedWords::Bus, GetLUALibraryFunctions);
|
|
|
|
auto syntaxSettings = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC("LUA Editor Text Settings", 0xb6e15565), AZ::UserSettings::CT_GLOBAL);
|
|
auto font = syntaxSettings->GetFont();
|
|
|
|
QList<QTextEdit::ExtraSelection> list;
|
|
QTextEdit::ExtraSelection selection;
|
|
selection.cursor = cursor;
|
|
QTextCharFormat textFormat;
|
|
textFormat.setFont(font);
|
|
textFormat.setBackground(syntaxSettings->GetCurrentIdentifierColor());
|
|
selection.format = textFormat;
|
|
|
|
QTBlockState mState;
|
|
mState.m_qtBlockState = -1;
|
|
m_machine->SetSaveState(mState);
|
|
|
|
QString searchString;
|
|
ParserStates matchState = ParserStates::Null;
|
|
|
|
int currentScopeBlock = -1;
|
|
|
|
QStringList luaNames;
|
|
for (auto block = document()->begin(); block != document()->end(); block = block.next())
|
|
{
|
|
auto text = block.text();
|
|
auto cursorPos = cursor.position() - block.position();
|
|
int delayedFold = 0;
|
|
m_machine->CaptureToken = [&](ParserStates state, int position, int length)
|
|
{
|
|
if (cursorPos >= position && cursorPos <= position + length)
|
|
{
|
|
if (state == ParserStates::Name)
|
|
{
|
|
QString match = text.mid(position, length);
|
|
AZStd::string dhText(match.toUtf8().constData());
|
|
if (!(keywords && keywords->find(dhText) != keywords->end()) &&
|
|
!(libraryFuncs && libraryFuncs->find(dhText) != libraryFuncs->end()))
|
|
{
|
|
searchString = AZStd::move(match);
|
|
matchState = state;
|
|
currentScopeBlock = block.blockNumber();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state == ParserStates::Name)
|
|
{
|
|
AZStd::string dhText(text.mid(position, length).toUtf8().constData());
|
|
if (m_LUAStartBlockKeywords.find(dhText) != m_LUAStartBlockKeywords.end())
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
}
|
|
if (m_LUAEndBlockKeywords.find(dhText) != m_LUAEndBlockKeywords.end())
|
|
{
|
|
m_machine->DecFoldLevel();
|
|
}
|
|
if (length > 1)
|
|
{
|
|
luaNames.push_back(text.mid(position, length));
|
|
}
|
|
}
|
|
};
|
|
auto startingState = m_machine->GetCurrentParserState();
|
|
m_machine->Parse(text);
|
|
while (delayedFold > 0)
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
--delayedFold;
|
|
}
|
|
|
|
auto endingState = m_machine->GetCurrentParserState();
|
|
if (startingState != ParserStates::LongComment && endingState == ParserStates::LongComment)
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
}
|
|
if (startingState != ParserStates::StringLiteral && endingState == ParserStates::StringLiteral) //should only be true if a long string literal
|
|
{
|
|
m_machine->IncFoldLevel();
|
|
}
|
|
if (startingState == ParserStates::LongComment && endingState != ParserStates::LongComment)
|
|
{
|
|
m_machine->DecFoldLevel();
|
|
}
|
|
if (startingState == ParserStates::StringLiteral && endingState != ParserStates::StringLiteral) //should only be true if a long string literal
|
|
{
|
|
m_machine->DecFoldLevel();
|
|
}
|
|
}
|
|
m_machine->SetOnIncFoldLevel([](int) {});
|
|
m_machine->SetOnDecFoldLevel([](int) {});
|
|
if (matchState != ParserStates::Null)
|
|
{
|
|
for (auto block = document()->begin(); block != document()->end(); block = block.next())
|
|
{
|
|
auto text = block.text();
|
|
|
|
m_machine->CaptureToken = [&](ParserStates state, int position, int length)
|
|
{
|
|
QString token = text.mid(position, length);
|
|
if (state == matchState)
|
|
{
|
|
if (token == searchString)
|
|
{
|
|
selection.cursor.setPosition(position + block.position());
|
|
selection.cursor.setPosition(position + block.position() + length, QTextCursor::MoveMode::KeepAnchor);
|
|
list.push_back(selection);
|
|
}
|
|
}
|
|
};
|
|
|
|
m_machine->Parse(text);
|
|
}
|
|
}
|
|
|
|
if (currentScopeBlock != -1 && currentScopeBlock != m_currentScopeBlock)
|
|
{
|
|
LUANamesInScopeChanged(luaNames);
|
|
m_currentScopeBlock = currentScopeBlock;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
QString LUASyntaxHighlighter::GetLUAName(const QTextCursor& cursor)
|
|
{
|
|
auto block = document()->findBlock(cursor.position());
|
|
if (!block.isValid())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
auto prevBlock = block.previous();
|
|
QTBlockState prevState;
|
|
if (prevBlock.isValid())
|
|
{
|
|
prevState.m_qtBlockState = prevBlock.userState();
|
|
}
|
|
else
|
|
{
|
|
prevState.m_qtBlockState = -1;
|
|
}
|
|
m_machine->SetSaveState(prevState);
|
|
m_machine->SetJoiningNames(true);
|
|
m_machine->SetOnIncFoldLevel([](int) {});
|
|
m_machine->SetOnDecFoldLevel([](int) {});
|
|
auto cursorPos = cursor.position() - block.position();
|
|
auto text = block.text();
|
|
|
|
QString result;
|
|
m_machine->CaptureToken = [&](ParserStates state, int position, int length)
|
|
{
|
|
if (cursorPos >= position && cursorPos <= position + length)
|
|
{
|
|
if (state == ParserStates::Name)
|
|
{
|
|
result = text.mid(position, length);
|
|
}
|
|
}
|
|
};
|
|
|
|
m_machine->Parse(block.text());
|
|
|
|
return result;
|
|
}
|
|
}
|