Files
imhex/plugins/builtin/source/content/text_highlighting/pattern_language.cpp
paxcut bfa9788099 impr: Various fixes and improvements to the pattern editor (#2559)
- fixed crash when utf8 chars were present in text editor
- fixed unable to scroll when cursor at line 1
- removed dependencies on thext editor that were not being used.

I had to go back to the old code (old for me) and fit in the changes
that were applied to the new code.That was only possible by
incorporating some of the new structural differences to the text editor.
This created new bugs and crashes that I ve have fixed but there may be
ones that I couldn't find in the very small amount of time I could spend
testing so that this commit wouldn't be delayed. If more crashes are
found due to the mixing of old and new code they should be resolved when
the new code is brought in.
2025-12-12 16:27:26 +01:00

2351 lines
95 KiB
C++

#include <content/text_highlighting/pattern_language.hpp>
#include <pl/core/ast/ast_node_type_decl.hpp>
#include <pl/core/ast/ast_node_enum.hpp>
#include <pl/core/tokens.hpp>
#include <hex/helpers/utils.hpp>
#include <wolv/utils/string.hpp>
#include <iostream>
#include <content/views/view_pattern_editor.hpp>
#include <pl/pattern_language.hpp>
#include <toasts/toast_notification.hpp>
namespace hex::plugin::builtin {
using namespace pl::core;
using Identifier = Token::Identifier;
using Keyword = Token::Keyword;
using Separator = Token::Separator;
using Operator = Token::Operator;
using Comment = Token::Comment;
using DocComment = Token::DocComment;
using Literal = Token::Literal;
using ValueType = Token::ValueType;
bool TextHighlighter::getIdentifierName(std::string &identifierName, Identifier *identifier) {
auto keyword = getValue<Keyword>(0);
identifier = getValue<Identifier>(0);
if (identifier != nullptr) {
identifierName = identifier->get();
return true;
} else if (keyword != nullptr) {
identifier = nullptr;
if (peek(tkn::Keyword::Parent)) {
identifierName = "Parent";
return true;
}
if (peek(tkn::Keyword::This)) {
identifierName = "This";
return true;
}
}
identifier = nullptr;
return false;
}
// Returns a chain of identifiers like a.b.c or a::b::c
bool TextHighlighter::getFullName(std::string &identifierName, std::vector<Identifier *> &identifiers, bool preserveCurr) {
Identifier *identifier = nullptr;
if (!peek(tkn::Literal::Identifier) || getTokenId(m_curr->location) < 1)
return getIdentifierName(identifierName, identifier);
forwardIdentifierName(identifierName, identifiers, preserveCurr);
return true;
}
bool TextHighlighter::forwardIdentifierName(std::string &identifierName, std::vector<Identifier *> &identifiers, bool preserveCurr ) {
auto curr = m_curr;
Identifier *identifier = getValue<Identifier>(0);
std::string current;
if (identifier != nullptr) {
identifiers.push_back(identifier);
identifierName += identifier->get();
} else if (getIdentifierName(current, identifier)) {
identifiers.push_back(identifier);
identifierName += current;
} else {
m_curr = curr;
return false;
}
skipArray(200, true);
while ((peek(tkn::Operator::ScopeResolution, 1) || peek(tkn::Separator::Dot, 1))) {
next();
if (peek(tkn::Operator::ScopeResolution))
identifierName += "::";
else if (peek(tkn::Separator::Dot))
identifierName += ".";
else {
m_curr = curr;
return false;
}
next();
if (getIdentifierName(current, identifier)) {
identifiers.push_back(identifier);
identifierName += current;
skipArray(200, true);
} else {
m_curr = curr;
return false;
}
}
if (preserveCurr)
m_curr = curr;
return true;
}
// Adds namespace if it exists
bool TextHighlighter::getQualifiedName(std::string &identifierName, std::vector<Identifier *> &identifiers, bool useDefinitions, bool preserveCurr) {
std::string shortName;
std::string qualifiedName;
if (!getFullName(identifierName, identifiers, preserveCurr))
return false;
if (std::ranges::find(m_UDTs, identifierName) != m_UDTs.end())
return true;
std::vector<std::string> vectorString;
if (identifierName.contains("::")) {
vectorString = wolv::util::splitString(identifierName, "::");
if (vectorString.size() > 1) {
shortName = vectorString.back();
vectorString.pop_back();
identifierName = wolv::util::combineStrings(vectorString, "::");
}
}
bool found = true;
for (auto name : vectorString) {
found = found || std::ranges::find(m_nameSpaces, name) != m_nameSpaces.end();
}
if (found) {
if (!shortName.empty())
identifierName = identifierName + "::" + shortName;
return true;
}
if (useDefinitions) {
if (m_functionDefinitions.contains(identifierName) || m_UDTDefinitions.contains(identifierName)) {
if (!shortName.empty())
identifierName = identifierName + "::" + shortName;
return true;
}
std::string nameSpace;
for (auto [name, definition]: m_UDTDefinitions) {
findNamespace(nameSpace, definition.tokenIndex);
if (!nameSpace.empty() && !identifierName.contains(nameSpace)) {
qualifiedName = nameSpace + "::" + identifierName;
if (name == qualifiedName) {
identifierName = qualifiedName;
if (!shortName.empty())
identifierName = identifierName + "::" + shortName;
return true;
}
}
if (name == identifierName) {
identifierName = name;
if (!shortName.empty())
identifierName = identifierName + "::" + shortName;
return true;
}
}
}
if (identifierName.empty())
return false;
return true;
}
// Finds the token range of a function, namespace or UDT
bool TextHighlighter::getTokenRange(std::vector<Token> keywords,
TextHighlighter::UnorderedBlocks &tokenRange,
TextHighlighter::OrderedBlocks &tokenRangeInv,
bool fullName, VariableScopes *blocks) {
bool addArgumentBlock = !fullName;
std::vector<i32> tokenStack;
if (getTokenId(m_curr->location) < 1)
return false;
std::string name;
if (fullName) {
std::vector<Identifier *> identifiers;
if (!getFullName(name, identifiers))
return false;
} else {
Identifier *identifier = nullptr;
if (!getIdentifierName(name, identifier))
return false;
std::string nameSpace;
findNamespace(nameSpace, getTokenId(m_curr->location));
if (!nameSpace.empty())
name = nameSpace + "::" + name;
}
i32 tokenCount = m_tokens.size();
auto saveCurr = m_curr - 1;
skipTemplate(200);
next();
if (sequence(tkn::Operator::Colon)) {
while (peek(tkn::Literal::Identifier)) {
std::vector<Identifier *> identifiers;
std::string identifierName;
if (!getFullName(identifierName, identifiers, false))
break;
if (std::ranges::find(m_inheritances[name], identifierName) == m_inheritances[name].end())
m_inheritances[name].push_back(identifierName);
skipTemplate(200);
next(2);
}
}
m_curr = saveCurr;
if (peek(tkn::ValueType::Auto))
next(-1);
i32 index1 = getTokenId(m_curr->location);
bool result = true;
for (auto keyword: keywords)
result = result && !peek(keyword);
if (result)
return false;
u32 nestedLevel = 0;
next();
auto endToken = TokenIter(m_tokens.begin() + tokenCount, m_tokens.end());
while (endToken > m_curr) {
if (sequence(tkn::Separator::LeftBrace)) {
auto tokenId = getTokenId(m_curr[-1].location);
tokenStack.push_back(tokenId);
nestedLevel++;
} else if (sequence(tkn::Separator::RightBrace)) {
nestedLevel--;
if (tokenStack.empty())
return false;
Interval range(tokenStack.back(), getTokenId(m_curr[-1].location));
tokenStack.pop_back();
if (nestedLevel == 0) {
range.end -= 1;
if (blocks != nullptr)
blocks->operator[](name).insert(range);
skipAttribute();
break;
}
if (blocks != nullptr)
blocks->operator[](name).insert(range);
} else if (sequence(tkn::Separator::EndOfProgram))
return false;
else
next();
}
i32 index2 = getTokenId(m_curr->location);
if (index2 > index1 && index2 < tokenCount) {
if (fullName) {
tokenRangeInv[Interval(index1, index2)] = name;
} else {
tokenRange[name] = Interval(index1, index2);
}
if (blocks != nullptr) {
if (addArgumentBlock) {
auto tokenIndex = blocks->operator[](name).begin()->start;
blocks->operator[](name).insert(Interval(index1, tokenIndex));
}
blocks->operator[](name).insert(Interval(index1, index2));
}
return true;
}
return false;
}
// Searches through tokens and loads all the ranges of one kind. First namespaces are searched.
void TextHighlighter::getAllTokenRanges(IdentifierType identifierTypeToSearch) {
if (m_tokens.empty())
return;
Identifier *identifier;
IdentifierType identifierType;
m_startToken = TokenIter(m_tokens.begin(), m_tokens.end());
auto endToken = TokenIter(m_tokens.end(), m_tokens.end());
for (m_curr = m_startToken; endToken > m_curr; next()) {
auto curr = m_curr;
if (peek(tkn::Literal::Identifier)) {
if (identifier = getValue<Identifier>(0); identifier != nullptr) {
identifierType = identifier->getType();
std::string name = identifier->get();
if (identifierType == identifierTypeToSearch) {
switch (identifierType) {
case IdentifierType::Function:
if (!m_functionTokenRange.contains(name))
getTokenRange({tkn::Keyword::Function}, m_functionTokenRange, m_namespaceTokenRange, false, &m_functionBlocks);
break;
case IdentifierType::NameSpace:
if (std::ranges::find(m_nameSpaces, name) == m_nameSpaces.end())
m_nameSpaces.push_back(name);
getTokenRange({tkn::Keyword::Namespace}, m_functionTokenRange, m_namespaceTokenRange, true, nullptr);
break;
case IdentifierType::UDT:
if (!m_UDTTokenRange.contains(name))
getTokenRange({tkn::Keyword::Struct, tkn::Keyword::Union, tkn::Keyword::Enum, tkn::Keyword::Bitfield}, m_UDTTokenRange, m_namespaceTokenRange, false, &m_UDTBlocks);
break;
case IdentifierType::Attribute:
linkAttribute();
break;
default:
break;
}
}
}
} else if (peek(tkn::Separator::EndOfProgram))
return;
m_curr = curr;
}
}
void TextHighlighter::skipDelimiters(i32 maxSkipCount, Token delimiter[2], i8 increment) {
auto curr = m_curr;
i32 skipCount = 0;
i32 depth = 0;
if (!isValid())
return;
i32 tokenId = getTokenId(m_curr->location);
auto tokenCount = m_tokens.size();
if (tokenId == -1 || tokenId >= (i32) tokenCount-1)
return;
i32 skipCountLimit =
increment > 0 ? std::min(maxSkipCount, (i32) tokenCount - 1 - tokenId) : std::min(maxSkipCount, tokenId);
next(increment);
skipCountLimit -= increment;
if (peek(delimiter[0])) {
next(increment);
skipCountLimit -= increment;
while (skipCount < skipCountLimit) {
if (peek(delimiter[1])) {
if (depth == 0)
return;
depth--;
} else if (peek(delimiter[0]))
depth++;
else if (peek(tkn::Separator::Semicolon)) {
if (increment < 0)
m_curr = curr;
return;
} else if (peek(tkn::Literal::Identifier)) {
if (peek(tkn::Separator::Dot,1) && peek(tkn::Literal::Identifier,2) )
m_memberChains.insert(getTokenId(m_curr->location));
else if (peek(tkn::Operator::ScopeResolution,1) && peek(tkn::Literal::Identifier,2))
m_scopeChains.insert(getTokenId(m_curr->location));
else
m_taggedIdentifiers.insert(getTokenId(m_curr->location));
}
next(increment);
skipCount++;
}
}
m_curr = curr;
return;
}
void TextHighlighter::skipTemplate(i32 maxSkipCount, bool forward) {
Token delimiters[2];
if (forward) {
delimiters[0] = Token(tkn::Operator::BoolLessThan);
delimiters[1] = Token(tkn::Operator::BoolGreaterThan);
} else {
delimiters[0] = Token(tkn::Operator::BoolGreaterThan);
delimiters[1] = Token(tkn::Operator::BoolLessThan);
}
skipDelimiters(maxSkipCount, delimiters, forward ? 1 : -1);
}
void TextHighlighter::skipArray(i32 maxSkipCount, bool forward) {
Token delimiters[2];
if (forward) {
delimiters[0] = Token(tkn::Separator::LeftBracket);
delimiters[1] = Token(tkn::Separator::RightBracket);
} else {
delimiters[0] = Token(tkn::Separator::RightBracket);
delimiters[1] = Token(tkn::Separator::LeftBracket);
}
skipDelimiters(maxSkipCount, delimiters, forward ? 1 : -1);
}
// Used to skip references,pointers,...
void TextHighlighter::skipToken(Token token, i8 step) {
if (peek(token, step))
next(step);
}
void TextHighlighter::skipAttribute() {
if (sequence(tkn::Separator::LeftBracket, tkn::Separator::LeftBracket)) {
while (!sequence(tkn::Separator::RightBracket, tkn::Separator::RightBracket))
next();
}
}
// Takes an identifier chain resolves the type of the end from the rest iteratively.
bool TextHighlighter::resolveIdentifierType(Definition &result, std::string identifierName) {
std::string separator;
if (identifierName.contains("::"))
separator = "::";
else
separator = ".";
auto vectorString = wolv::util::splitString(identifierName, separator);
std::string nameSpace;
u32 index = 0;
std::string currentName = vectorString[index];
index++;
std::string variableParentType;
Definition definition;
if (vectorString.size() > 1) {
if (findIdentifierDefinition(definition, currentName)) {
variableParentType = definition.typeStr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, definition.idType);
skipArray(200, true);
next();
} else
return false;
}
while (index < vectorString.size()) {
if ( separator == ".") {
currentName = vectorString[index];
next();
if (findIdentifierDefinition(result, currentName, variableParentType)) {
variableParentType = result.typeStr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, result.idType);
skipArray(200, true);
next();
} else
return false;
} else if (separator == "::") {
next();
if (std::ranges::find(m_nameSpaces, currentName) != m_nameSpaces.end()) {
nameSpace += currentName + "::";
variableParentType = vectorString[index];
currentName = variableParentType;
} else if (std::ranges::find(m_UDTs, currentName) != m_UDTs.end()) {
variableParentType = currentName;
if (!nameSpace.empty() && !variableParentType.contains(nameSpace))
variableParentType = nameSpace + variableParentType;
else if (findNamespace(nameSpace) && !variableParentType.contains(nameSpace))
variableParentType = nameSpace + "::" + variableParentType;
currentName = vectorString[index];
if (findIdentifierDefinition(result, currentName, variableParentType)) {
variableParentType = result.typeStr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, result.idType);
skipArray(200, true);
next();
} else
return false;
}
}
index++;
}
return true;
}
// If contex then find it otherwise check if it belongs in map
bool TextHighlighter::findOrContains(std::string &context, UnorderedBlocks tokenRange, VariableMap variableMap) {
if (context.empty())
return findScope(context, tokenRange);
else
return variableMap.contains(context);
}
void TextHighlighter::setBlockInstancesColor(const std::string &name, const Definition &definition, const Interval &block) {
if (definition.idType == IdentifierType::Unknown)
return;
for (auto instance: m_instances[name]) {
if (block.contains(instance)) {
if (auto identifier = std::get_if<Identifier>(&m_tokens[instance].value);
identifier != nullptr && identifier->getType() == IdentifierType::Unknown)
setIdentifierColor(instance, definition.idType);
}
}
}
bool TextHighlighter::findIdentifierDefinition( Definition &result, const std::string &optionalIdentifierName, std::string optionalName, bool setInstances) {
auto curr = m_curr;
bool isFunction = false;
auto tokenId = getTokenId(m_curr->location);
std::vector<Definition> definitions;
std::string name = optionalName;
result.idType = IdentifierType::Unknown;
std::string identifierName = optionalIdentifierName;
if (optionalIdentifierName == "") {
std::vector<Identifier *> identifiers;
getFullName(identifierName, identifiers);
}
Interval tokenRange;
Scopes blocks;
Scopes::iterator blocksIterBegin, blocksIterEnd;
if (findOrContains(name, m_UDTTokenRange, m_UDTVariables) && m_UDTVariables[name].contains(identifierName)) {
definitions = m_UDTVariables[name][identifierName];
tokenRange = m_UDTTokenRange[name];
blocksIterBegin = m_UDTBlocks[name].begin();
blocksIterEnd = m_UDTBlocks[name].end();
} else if (findOrContains(name, m_functionTokenRange, m_functionVariables) &&
m_functionVariables[name].contains(identifierName)) {
isFunction = true;
definitions = m_functionVariables[name][identifierName];
tokenRange = m_functionTokenRange[name];
blocksIterBegin = m_functionBlocks[name].begin();
blocksIterEnd = m_functionBlocks[name].end();
--blocksIterEnd;
} else if (m_globalVariables.contains(identifierName)) {
definitions = m_globalVariables[identifierName];
tokenRange = Interval(0, m_tokens.size());
blocks.insert(tokenRange);
blocksIterBegin = blocks.begin();
blocksIterEnd = blocks.end();
} else if (name == "hex::type::Json" || name == "Object") {
result.idType = IdentifierType::LocalVariable;
result.typeStr = "Object";
return true;
}
if (isFunction) {
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
if (tokenId > block->start && tokenId < block->end) {
blocksIterBegin = block;
break;
}
}
for (auto definition : definitions) {
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
if (definition.tokenIndex > block->start && definition.tokenIndex < block->end) {
result = definition;
m_curr = curr;
if (setInstances)
setBlockInstancesColor(identifierName, definition, *block);
return true;
}
}
}
auto it = std::find_if(definitions.begin(), definitions.end(), [&](const Definition &definition) {
return definition.tokenIndex > tokenRange.start && definition.tokenIndex < tokenRange.end;
});
if (it != definitions.end()) {
result = *it;
m_curr = curr;
if (setInstances)
setBlockInstancesColor(identifierName, *it, tokenRange);
return true;
}
} else {
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
if (tokenId > block->start && tokenId < block->end) {
blocksIterBegin = block;
break;
}
}
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
for (auto definition: definitions) {
if (definition.tokenIndex > block->start && definition.tokenIndex < block->end) {
result = definition;
m_curr = curr;
if (setInstances)
setBlockInstancesColor(identifierName, definition, *block);
return true;
}
}
}
}
m_curr = curr;
return false;
}
using Definition = TextHighlighter::Definition;
bool TextHighlighter::colorOperatorDotChain() {
std::vector<Identifier *> identifiers;
std::string variableName;
auto tokenCount = m_tokens.size();
if (!getQualifiedName(variableName, identifiers, true))
return false;
auto vectorString = wolv::util::splitString(variableName, ".");
u32 index = 0;
u32 currentLine = m_curr->location.line - 1;
u32 startingLineTokenIndex = m_firstTokenIdOfLine[currentLine];
if (startingLineTokenIndex == 0xFFFFFFFFu || startingLineTokenIndex > tokenCount)
return false;
if (auto *keyword = std::get_if<Keyword>(&m_tokens[startingLineTokenIndex].value);
keyword != nullptr && *keyword == Keyword::Import) {
while (index < vectorString.size()) {
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, IdentifierType::NameSpace);
next(2);
index++;
}
return true;
} else {
std::string variableParentType;
Definition definition;
std::string currentName = vectorString[index];
index++;
bool brokenChain = false;
if (findIdentifierDefinition(definition, currentName)) {
variableParentType = definition.typeStr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, definition.idType);
skipArray(200, true);
next();
} else {
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, IdentifierType::Unknown);
skipArray(200, true);
next();
brokenChain = true;
}
while (index < vectorString.size()) {
currentName = vectorString[index];
next();
Definition result=definition;
Definition parentDefinition = result;
if (findIdentifierDefinition(result, currentName, variableParentType) && !brokenChain) {
variableParentType = result.typeStr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, result.idType);
skipArray(200, true);
next();
} else if (auto udtVars = m_UDTVariables[result.typeStr];udtVars.contains(vectorString[index-1])) {
auto saveCurr = m_curr;
std::string templateName = "";
auto instances = m_instances[variableParentType];
for (auto instance : instances) {
if (auto *identifier = std::get_if<Identifier>(&m_tokens[instance].value); identifier != nullptr && identifier->getType() == IdentifierType::TemplateArgument) {
auto tokenRange = m_UDTTokenRange[result.typeStr];
auto tokenIndex = m_firstTokenIdOfLine[getLocation(parentDefinition.tokenIndex).line - 1];
i32 argNumber = getArgumentNumber(tokenRange.start, instance);
getTokenIdForArgument(tokenIndex, argNumber, tkn::Operator::BoolLessThan);
if (auto *identifier2 = std::get_if<Identifier>(&m_curr->value); identifier2 != nullptr) {
templateName = identifier2->get();
break;
}
}
}
if (!templateName.empty() && findIdentifierDefinition(result, currentName, templateName) ) {
variableParentType = result.typeStr;
m_curr = saveCurr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, result.idType);
skipArray(200, true);
next();
} else{
if (m_typeDefMap.contains(variableParentType)) {
std::string typeName = "";
instances = m_instances[variableParentType];
for (auto instance: instances) {
if (auto *identifier = std::get_if<Identifier>(&m_tokens[instance].value);
identifier != nullptr && identifier->getType() == IdentifierType::Typedef) {
if (auto *identifier2 = std::get_if<Identifier>(&m_tokens[instance + 2].value);
identifier2 != nullptr) {
typeName = identifier2->get();
break;
}
}
}
if (!typeName.empty() && findIdentifierDefinition(result, currentName, typeName)) {
variableParentType = result.typeStr;
m_curr = saveCurr;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, result.idType);
skipArray(200, true);
next();
}
}
}
} else {
brokenChain = true;
auto tokenIndex = getTokenId(m_curr->location);
setIdentifierColor(tokenIndex, IdentifierType::Unknown);
skipArray(200, true);
next();
}
index++;
}
}
return true;
}
bool TextHighlighter::colorSeparatorScopeChain() {
std::vector<Identifier *> identifiers;
std::string identifierName;
if (!getQualifiedName(identifierName, identifiers, true))
return false;
auto tokenCount = m_tokens.size();
auto vectorString = wolv::util::splitString(identifierName, "::");
auto vectorStringCount = vectorString.size();
if (identifiers.size() != vectorStringCount)
return false;
auto curr = m_curr;
std::string nameSpace;
for (u32 i = 0; i < vectorStringCount ; i++) {
auto name = vectorString[i];
auto identifier = identifiers[i];
if (std::ranges::find(m_nameSpaces, name) != m_nameSpaces.end()) {
setIdentifierColor(-1, IdentifierType::NameSpace);
nameSpace += name + "::";
} else if (m_UDTDefinitions.contains(nameSpace+name)) {
name = nameSpace + name;
auto udtDefinition = m_UDTDefinitions[name];
auto definitionIndex = udtDefinition.tokenIndex-1;
if (auto *keyword = std::get_if<Keyword>(&m_tokens[definitionIndex].value); keyword != nullptr) {
setIdentifierColor(-1, IdentifierType::UDT);
if (*keyword == Keyword::Enum) {
next();
if (!sequence(tkn::Operator::ScopeResolution) || vectorStringCount != i+2 ||
!m_UDTVariables.contains(name))
return false;
auto variableName = vectorString[i+1];
if (!m_UDTVariables[name].contains(variableName))
return false;
auto variableDefinition = m_UDTVariables[name][variableName][0];
setIdentifierColor(-1, variableDefinition.idType);
return true;
} else
return true;
} else
return false;
} else if (identifier->getType() == IdentifierType::Function) {
setIdentifierColor(-1, IdentifierType::Function);
return true;
} else if (std::ranges::find(m_UDTs, nameSpace+name) != m_UDTs.end()) {
setIdentifierColor(-1, IdentifierType::UDT);
if (vectorStringCount == i+1)
return true;
next();
if (!sequence(tkn::Operator::ScopeResolution) || vectorStringCount != i+2)
return false;
setIdentifierColor(-1, IdentifierType::PatternVariable);
return true;
} else
return false;
next(2);
}
m_curr = curr;
if (std::ranges::find(m_nameSpaces, identifierName) != m_nameSpaces.end()) {
setIdentifierColor(-1, IdentifierType::NameSpace);
return true;
}
i32 index = getTokenId(m_curr->location);
if (index < (i32) tokenCount - 1 && index > 2) {
auto nextToken = m_curr[1];
auto *separator = std::get_if<Token::Separator>(&nextToken.value);
auto *operatortk = std::get_if<Token::Operator>(&nextToken.value);
if ((separator != nullptr && *separator == Separator::Semicolon) ||
(operatortk != nullptr && *operatortk == Operator::BoolLessThan)) {
auto previousToken = m_curr[-1];
auto prevprevToken = m_curr[-2];
operatortk = std::get_if<Operator>(&previousToken.value);
auto *identifier2 = std::get_if<Identifier>(&prevprevToken.value);
if (operatortk != nullptr && identifier2 != nullptr && *operatortk == Operator::ScopeResolution) {
if (identifier2->getType() == IdentifierType::UDT) {
setIdentifierColor(-1, IdentifierType::LocalVariable);
return true;
} else if (identifier2->getType() == IdentifierType::NameSpace) {
setIdentifierColor(-1, IdentifierType::UDT);
return true;
}
}
}
}
return false;
}
// finds the name of the token range that the given or the current token index is in.
bool TextHighlighter::findScope(std::string &name, const UnorderedBlocks &map, i32 optionalTokenId) {
auto tokenId = optionalTokenId ==-1 ? getTokenId(m_curr->location) : optionalTokenId;
for (auto [scopeName, range]: map) {
if (range.contains(tokenId)) {
name = scopeName;
return true;
}
}
return false;
}
// Finds the namespace of the given or the current token index.
bool TextHighlighter::findNamespace(std::string &nameSpace, i32 optionalTokenId) {
nameSpace = "";
for (auto [interval, name]: m_namespaceTokenRange) {
i32 tokenId = optionalTokenId == -1 ? getTokenId(m_curr->location) : optionalTokenId;
if (tokenId > interval.start && tokenId < interval.end) {
if (nameSpace == "")
nameSpace = name;
else
nameSpace = name + "::" + nameSpace;
}
}
if (nameSpace != "")
return true;
return false;
}
//The context is the name of the function or UDT that the variable is in.
std::string TextHighlighter::findIdentifierTypeStr(const std::string &identifierName, std::string context) {
Definition result;
findIdentifierDefinition(result, identifierName, context);
return result.typeStr;
}
//The context is the name of the function or UDT that the variable is in.
TextHighlighter::IdentifierType TextHighlighter::findIdentifierType(const std::string &identifierName, std::string context) {
Definition result;
findIdentifierDefinition(result, identifierName, context);
return result.idType;
}
// Creates a map from the attribute function to the type of the argument it takes.
void TextHighlighter::linkAttribute() {
auto curr = m_curr;
bool qualifiedAttribute = false;
using Types = std::map<std::string, pl::hlp::safe_shared_ptr<pl::core::ast::ASTNodeTypeDecl>>;
auto parser = patternLanguage->get()->getInternals().parser.get();
Types types = parser->getTypes();
while (sequence(tkn::Literal::Identifier, tkn::Operator::ScopeResolution))
qualifiedAttribute = true;
if (qualifiedAttribute) {
auto identifier = getValue<Identifier>(0);
if (identifier != nullptr)
setIdentifierColor(-1, IdentifierType::Attribute);
m_curr = curr;
identifier = getValue<Identifier>(0);
if (identifier != nullptr)
setIdentifierColor(-1, IdentifierType::NameSpace);
} else
m_curr = curr;
std::string functionName;
next();
if (sequence(tkn::Separator::LeftParenthesis, tkn::Literal::String)) {
functionName = getValue<Literal>(-1)->toString(false);
if (!functionName.contains("::")) {
std::string namespaceName;
if (findNamespace(namespaceName))
functionName = namespaceName + "::" + functionName;
} else {
auto vectorString = wolv::util::splitString(functionName, "::");
vectorString.pop_back();
for (auto nameSpace: vectorString) {
if (std::ranges::find(m_nameSpaces, nameSpace) == m_nameSpaces.end())
m_nameSpaces.push_back(nameSpace);
}
}
} else
return;
u32 line = m_curr->location.line;
i32 tokenIndex;
while (!peek(tkn::Separator::Semicolon, -1)) {
if (line = previousLine(line); line > m_firstTokenIdOfLine.size()-1)
return;
if (tokenIndex = m_firstTokenIdOfLine[line]; !isTokenIdValid(tokenIndex))
return;
m_curr = m_startToken;
next(tokenIndex);
while (peek(tkn::Literal::Comment, -1) || peek(tkn::Literal::DocComment, -1))
next(-1);
}
while (peek(tkn::Literal::Comment) || peek(tkn::Literal::DocComment))
next();
Identifier *identifier;
std::string UDTName;
while (sequence(tkn::Literal::Identifier, tkn::Operator::ScopeResolution)) {
identifier = getValue<Identifier>(-2);
UDTName += identifier->get() + "::";
}
if (sequence(tkn::Literal::Identifier)) {
identifier = getValue<Identifier>(-1);
UDTName += identifier->get();
if (!UDTName.contains("::")) {
std::string namespaceName;
if (findNamespace(namespaceName))
UDTName = namespaceName + "::" + UDTName;
}
if (types.contains(UDTName))
m_attributeFunctionArgumentType[functionName] = UDTName;
} else if (sequence(tkn::ValueType::Any)) {
auto valueType = getValue<ValueType>(-1);
m_attributeFunctionArgumentType[functionName] = Token::getTypeName(*valueType);
} else {
if (findScope(UDTName, m_UDTTokenRange) && !UDTName.empty())
m_attributeFunctionArgumentType[functionName] = UDTName;
}
}
// This function assumes that the first variable in the link that concatenates sequences including the Parent keyword started with Parent and was removed. Uses a function to find
// all the parents of a variable, If there are subsequent elements in the link that are Parent then for each parent it finds all the grandparents and puts them in a vector called
// parentTypes. It stops when a link that's not Parent is found amd only returns the last generation of parents.
bool TextHighlighter::findAllParentTypes(std::vector<std::string> &parentTypes,
std::vector<Identifier *> &identifiers, std::string &optionalFullName) {
auto fullName = optionalFullName;
if (optionalFullName.empty())
forwardIdentifierName(fullName, identifiers);
auto nameParts = wolv::util::splitString(fullName, ".");
std::vector<std::string> grandpaTypes;
findParentTypes(parentTypes);
if (parentTypes.empty())
return false;
auto currentName = nameParts[0];
nameParts.erase(nameParts.begin());
auto identifier = identifiers[0];
identifiers.erase(identifiers.begin());
while (currentName == "Parent" && !nameParts.empty()) {
for (auto parentType: parentTypes)
findParentTypes(grandpaTypes, parentType);
currentName = nameParts[0];
nameParts.erase(nameParts.begin());
identifier = identifiers[0];
identifiers.erase(identifiers.begin());
parentTypes = grandpaTypes;
grandpaTypes.clear();
}
nameParts.insert(nameParts.begin(), currentName);
identifiers.insert(identifiers.begin(), identifier);
optionalFullName = wolv::util::combineStrings(nameParts, ".");
return true;
}
// Searches for parents through every custom type,i.e. for structs that have members
// of the same type as the one being searched and places them in a vector called parentTypes.
bool TextHighlighter::findParentTypes(std::vector<std::string> &parentTypes, const std::string &optionalUDTName) {
std::string UDTName;
std::string functionName;
bool isFunction = false;
if (optionalUDTName.empty()) {
if (!findScope(UDTName, m_UDTTokenRange)) {
if (!findScope(functionName, m_functionTokenRange)) {
return false;
} else {
isFunction = true;
}
}
} else
UDTName = optionalUDTName;
bool found = false;
if (!isFunction) {
for (auto [name, variables]: m_UDTVariables) {
for (auto [variableName, definitions]: variables) {
for (auto definition: definitions) {
if (definition.typeStr == UDTName) {
if (std::ranges::find(parentTypes, name) == parentTypes.end()) {
parentTypes.push_back(name);
found = true;
}
}
}
}
}
} else {
auto curr = m_curr;
for (auto [name, range] : m_UDTTokenRange) {
auto startToken = TokenIter(m_tokens.begin()+range.start,m_tokens.begin()+range.end);
auto endToken = TokenIter(m_tokens.begin()+range.end,m_tokens.end());
for ( m_curr = startToken; endToken > m_curr; next()) {
if (auto *identifier = std::get_if<Identifier>(&m_curr->value); identifier != nullptr) {
auto identifierName = identifier->get();
auto identifierType = identifier->getType();
if (identifierName == functionName && identifierType == IdentifierType::Function) {
parentTypes.push_back(name);
found = true;
}
}
}
}
m_curr = curr;
}
return found;
}
// this function searches all the parents recursively until it can match the variable name at the end of the chain
// and selects its type to colour the variable because the search only occurs pn type declarations which we know
// the types of. Once the end link is found then all the previous links are also assigned the types that were found
// for them during the search.
bool TextHighlighter::tryParentType(const std::string &parentType, std::string &variableName,
std::optional<Definition> &result,
std::vector<Identifier *> &identifiers) {
auto vectorString = wolv::util::splitString(variableName, ".");
auto count = vectorString.size();
auto UDTName = parentType;
auto currentName = vectorString[0];
if (m_UDTVariables.contains(UDTName) && m_UDTVariables[UDTName].contains(currentName)) {
auto definitions = m_UDTVariables[UDTName][currentName];
for (auto definition: definitions) {
UDTName = definition.typeStr;
if (count == 1) {
setIdentifierColor(-1, definition.idType);
result = definition;
return true;
}
vectorString.erase(vectorString.begin());
variableName = wolv::util::combineStrings(vectorString, ".");
Identifier *identifier = identifiers[0];
identifiers.erase(identifiers.begin());
skipArray(200, true);
next(2);
if (tryParentType(UDTName, variableName, result, identifiers)) {
next(-1);
skipArray(200, false);
next(-1);
setIdentifierColor(-1, definition.idType);
return true;
}
identifiers.insert(identifiers.begin(), identifier);
variableName += "." + currentName;
next(-1);
skipArray(200, false);
next(-1);
}
return false;
} else
return false;
return false;
}
// Handles Parent keyword.
std::optional<Definition> TextHighlighter::setChildrenTypes() {
auto curr = m_curr;
std::string fullName;
std::vector<Identifier *> identifiers;
std::vector<Definition> definitions;
std::optional<Definition> result;
forwardIdentifierName(fullName, identifiers);
std::vector<std::string> parentTypes;
auto vectorString = wolv::util::splitString(fullName, ".");
if (vectorString[0] == "Parent") {
vectorString.erase(vectorString.begin());
fullName = wolv::util::combineStrings(vectorString, ".");
identifiers.erase(identifiers.begin());
if (!findAllParentTypes(parentTypes, identifiers, fullName)) {
m_curr = curr;
return std::nullopt;
}
} else {
m_curr = curr;
return std::nullopt;
}
for (auto parentType: parentTypes) {
m_curr = curr;
while (peek(tkn::Keyword::Parent))
next(2);
if (tryParentType(parentType, fullName, result, identifiers)) {
if (result.has_value())
definitions.push_back(result.value());
} else {
m_curr = curr;
return std::nullopt;
}
}
// Todo: Are all definitions supposed to be the same? If not, which one should be used?
// for now, use the first one.
if (!definitions.empty())
result = definitions[0];
m_curr = curr;
return result;
}
const TextHighlighter::TokenTypeColor TextHighlighter::m_tokenTypeColor = {
{Token::Type::Keyword, ui::TextEditor::PaletteIndex::Keyword},
{Token::Type::ValueType, ui::TextEditor::PaletteIndex::BuiltInType},
{Token::Type::Operator, ui::TextEditor::PaletteIndex::Operator},
{Token::Type::Separator, ui::TextEditor::PaletteIndex::Separator},
{Token::Type::String, ui::TextEditor::PaletteIndex::StringLiteral},
{Token::Type::Directive, ui::TextEditor::PaletteIndex::Directive},
{Token::Type::Comment, ui::TextEditor::PaletteIndex::Comment},
{Token::Type::Integer, ui::TextEditor::PaletteIndex::NumericLiteral},
{Token::Type::Identifier, ui::TextEditor::PaletteIndex::Identifier},
{Token::Type::DocComment, ui::TextEditor::PaletteIndex::DocComment}
};
const TextHighlighter::IdentifierTypeColor TextHighlighter::m_identifierTypeColor = {
{Identifier::IdentifierType::Macro, ui::TextEditor::PaletteIndex::PreprocIdentifier},
{Identifier::IdentifierType::UDT, ui::TextEditor::PaletteIndex::UserDefinedType},
{Identifier::IdentifierType::Function, ui::TextEditor::PaletteIndex::Function},
{Identifier::IdentifierType::Attribute, ui::TextEditor::PaletteIndex::Attribute},
{Identifier::IdentifierType::NameSpace, ui::TextEditor::PaletteIndex::NameSpace},
{Identifier::IdentifierType::Typedef, ui::TextEditor::PaletteIndex::TypeDef},
{Identifier::IdentifierType::PatternVariable, ui::TextEditor::PaletteIndex::PatternVariable},
{Identifier::IdentifierType::LocalVariable, ui::TextEditor::PaletteIndex::LocalVariable},
{Identifier::IdentifierType::CalculatedPointer, ui::TextEditor::PaletteIndex::CalculatedPointer},
{Identifier::IdentifierType::TemplateArgument, ui::TextEditor::PaletteIndex::TemplateArgument},
{Identifier::IdentifierType::PlacedVariable, ui::TextEditor::PaletteIndex::PlacedVariable},
{Identifier::IdentifierType::View, ui::TextEditor::PaletteIndex::View},
{Identifier::IdentifierType::FunctionVariable, ui::TextEditor::PaletteIndex::FunctionVariable},
{Identifier::IdentifierType::FunctionParameter, ui::TextEditor::PaletteIndex::FunctionParameter},
{Identifier::IdentifierType::Unknown, ui::TextEditor::PaletteIndex::UnkIdentifier},
{Identifier::IdentifierType::FunctionUnknown, ui::TextEditor::PaletteIndex::UnkIdentifier},
{Identifier::IdentifierType::MemberUnknown, ui::TextEditor::PaletteIndex::UnkIdentifier},
{Identifier::IdentifierType::ScopeResolutionUnknown, ui::TextEditor::PaletteIndex::UnkIdentifier},
{Identifier::IdentifierType::GlobalVariable, ui::TextEditor::PaletteIndex::GlobalVariable},
};
// Second paletteIndex called from processLineTokens to process literals
ui::TextEditor::PaletteIndex TextHighlighter::getPaletteIndex(Literal *literal) {
if (literal->isFloatingPoint() || literal->isSigned() || literal->isUnsigned())
return ui::TextEditor::PaletteIndex::NumericLiteral;
else if (literal->isCharacter() || literal->isBoolean()) return ui::TextEditor::PaletteIndex::CharLiteral;
else if (literal->isString()) return ui::TextEditor::PaletteIndex::StringLiteral;
else return ui::TextEditor::PaletteIndex::Default;
}
// Render the compilation errors using squiggly lines
void TextHighlighter::renderErrors() {
const auto processMessage = [](const auto &message) {
auto lines = wolv::util::splitString(message, "\n");
std::ranges::transform(lines, lines.begin(), [](auto line) {
if (line.size() >= 128)
line = wolv::util::trim(line);
return hex::limitStringLength(line, 128);
});
return wolv::util::combineStrings(lines, "\n");
};
ui::TextEditor::ErrorMarkers errorMarkers;
if (!m_compileErrors.empty()) {
for (const auto &error: m_compileErrors) {
if (isLocationValid(error.getLocation())) {
auto key = ui::TextEditor::Coordinates(error.getLocation().line, error.getLocation().column);
if (!errorMarkers.contains(key) || errorMarkers[key].first < (i32) error.getLocation().length)
errorMarkers[key] = std::make_pair(error.getLocation().length, processMessage(error.getMessage()));
}
}
}
ui::TextEditor *editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
editor->setErrorMarkers(errorMarkers);
else
log::warn("Text editor not found, provider is null");
}
// creates a map from variable names to a vector of token indices
// od every instance of the variable name in the code.
void TextHighlighter::setInitialColors() {
m_startToken = m_originalPosition = m_partOriginalPosition = TokenIter(m_tokens.begin(), m_tokens.end());
auto endToken = TokenIter(m_tokens.end(), m_tokens.end());
for (m_curr = m_startToken; endToken > m_curr; next()) {
if (peek(tkn::Literal::Identifier)) {
if (auto identifier = getValue<Identifier>(0); identifier != nullptr) {
if (auto identifierType = identifier->getType(); identifierType != IdentifierType::Unknown && identifierType != IdentifierType::MemberUnknown &&
identifierType != IdentifierType::FunctionUnknown && identifierType != IdentifierType::ScopeResolutionUnknown) {
setIdentifierColor(-1, identifierType);
}
}
} else if (peek(tkn::Separator::EndOfProgram))
return;
}
}
void TextHighlighter::loadInstances() {
std::map<std::string, std::vector<i32>> instances;
m_startToken = m_originalPosition = m_partOriginalPosition = TokenIter(m_tokens.begin(), m_tokens.end());
auto endToken = TokenIter(m_tokens.end(), m_tokens.end());
for (m_curr = m_startToken; endToken > m_curr; next()) {
if (peek(tkn::Literal::Identifier)) {
std::string name;
if (auto identifier = getValue<Identifier>(0); identifier != nullptr) {
if (auto identifierType = identifier->getType(); identifierType != IdentifierType::Unknown && identifierType != IdentifierType::MemberUnknown &&
identifierType != IdentifierType::FunctionUnknown && identifierType != IdentifierType::ScopeResolutionUnknown) {
name = identifier->get();
if (identifierType == IdentifierType::Typedef) {
auto curr = m_curr;
next();
std::string typeName = "";
if (sequence(tkn::Operator::Assign, tkn::Literal::Identifier)) {
auto identifier2 = getValue<Identifier>(-1);
if (identifier2 != nullptr) {
typeName = identifier2->get();
if (!m_typeDefMap.contains(name) && !typeName.empty()) {
m_typeDefMap[name] = typeName;
m_typeDefInvMap[typeName] = name;
}
}
}
m_curr = curr;
}
} else {
name = identifier->get();
auto curr = m_curr;
auto tokenIndex = getTokenId(m_curr->location);
skipArray(200, true);
next();
bool chainStarted = false;
while (sequence(tkn::Operator::ScopeResolution, tkn::Literal::Identifier)) {
if (identifier = getValue<Identifier>(-1); identifier != nullptr)
name += "::" + identifier->get();
if (!chainStarted) {
chainStarted = true;
m_scopeChains.insert(tokenIndex);
}
curr = m_curr;
}
while (sequence(tkn::Separator::Dot, tkn::Literal::Identifier)) {
if (identifier = getValue<Identifier>(-1); identifier != nullptr)
name += "." + identifier->get();
if (!chainStarted) {
chainStarted = true;
m_memberChains.insert(tokenIndex);
}
skipArray(200, true);
curr = m_curr;
}
m_curr = curr;
}
}
auto id = getTokenId(m_curr->location);
if (instances.contains(name)) {
auto &nameInstances = instances[name];
if (std::ranges::find(nameInstances, id) == nameInstances.end())
nameInstances.push_back(id);
} else
instances[name].push_back(id);
} else if (peek(tkn::Separator::EndOfProgram))
break;
}
m_instances = std::move(instances);
}
// Get the location of a given token index
pl::core::Location TextHighlighter::getLocation(i32 tokenId) {
if (tokenId >= (i32) m_tokens.size())
return Location::Empty();
return m_tokens[tokenId].location;
}
// Get the token index for a given location.
i32 TextHighlighter::getTokenId(pl::core::Location location) {
if (!isLocationValid(location))
return -1;
auto line1 = location.line - 1;
auto line2 = nextLine(line1);
auto tokenCount = m_tokens.size();
i32 tokenStart = m_firstTokenIdOfLine[line1];
i32 tokenEnd = m_firstTokenIdOfLine[line2] - 1;
if (tokenEnd >= (i32) tokenCount)
tokenEnd = tokenCount - 1;
if (tokenStart == -1 || tokenEnd == -1 || tokenStart >= (i32) tokenCount)
return -1;
for (i32 i = tokenStart; i <= tokenEnd; i++) {
if (m_tokens[i].location.column >= location.column)
return i;
}
return -1;
}
void TextHighlighter::setIdentifierColor(i32 tokenId, const IdentifierType &type) {
Token *token;
if (tokenId == -1)
token = const_cast<Token *>(&m_curr[0]);
else
token = const_cast<Token *>(&m_tokens[tokenId]);
if (token->type == Token::Type::Identifier && (!m_tokenColors.contains(token) || m_tokenColors.at(token) == ui::TextEditor::PaletteIndex::Default || m_tokenColors.at(token) == ui::TextEditor::PaletteIndex::UnkIdentifier))
m_tokenColors[token] = m_identifierTypeColor.at(type);
else if (!m_tokenColors.contains(token))
m_tokenColors[token] = m_tokenTypeColor.at(token->type);
}
void TextHighlighter::setColor(i32 tokenId, const IdentifierType &type) {
Token *token;
if (tokenId == -1)
token = const_cast<Token *>(&m_curr[0]);
else
token = const_cast<Token *>(&m_tokens[tokenId]);
if (token->type == Token::Type::Integer) {
auto literal = getValue<Literal>(0);
if (literal != nullptr && !m_tokenColors.contains(token))
m_tokenColors[token] = getPaletteIndex(literal);
} else if (token->type == Token::Type::DocComment) {
auto docComment = getValue<DocComment>(0);
if (docComment != nullptr && !m_tokenColors.contains(token)) {
if (docComment->singleLine)
m_tokenColors[token] = ui::TextEditor::PaletteIndex::DocComment;
else if (docComment->global)
m_tokenColors[token] = ui::TextEditor::PaletteIndex::GlobalDocComment;
else
m_tokenColors[token] = ui::TextEditor::PaletteIndex::DocBlockComment;
}
} else if (token->type == Token::Type::Comment) {
auto comment = getValue<Token::Comment>(0);
if (comment != nullptr && !m_tokenColors.contains(token)) {
if (comment->singleLine)
m_tokenColors[token] = ui::TextEditor::PaletteIndex::Comment;
else
m_tokenColors[token] = ui::TextEditor::PaletteIndex::BlockComment;
}
} else
setIdentifierColor(tokenId, type);
}
void TextHighlighter::colorRemainingIdentifierTokens() {
std::vector<i32> taggedIdentifiers;
for (auto index: m_taggedIdentifiers) {
taggedIdentifiers.push_back(index);
}
m_taggedIdentifiers.clear();
m_startToken = m_originalPosition = m_partOriginalPosition = TokenIter(m_tokens.begin(), m_tokens.end());
auto endToken = TokenIter(m_tokens.end(), m_tokens.end());
m_curr = m_startToken;
while (endToken > m_curr) {
if (peek(tkn::Separator::EndOfProgram))
return;
i32 tokenId = getTokenId(m_curr->location);
if (!taggedIdentifiers.empty() && tokenId > taggedIdentifiers.back()) {
next(taggedIdentifiers.back() - tokenId);
taggedIdentifiers.pop_back();
}
Token *token = const_cast<Token *>(&m_curr[0]);
if (sequence(tkn::Keyword::Import, tkn::Literal::Identifier)) {
next(-1);
do {
if (auto identifier = const_cast<Identifier *>(getValue<Token::Identifier>(0)); identifier != nullptr) {
setIdentifierColor(-1, IdentifierType::NameSpace);
if (std::ranges::find(m_nameSpaces, identifier->get()) == m_nameSpaces.end()) {
m_nameSpaces.push_back(identifier->get());
}
}
} while (sequence(tkn::Literal::Identifier,tkn::Separator::Dot));
next();
if (sequence(tkn::Keyword::As, tkn::Literal::Identifier)) {
next(-1);
if (auto identifier = const_cast<Identifier *>(getValue<Token::Identifier>(0)); identifier != nullptr) {
setIdentifierColor(-1, IdentifierType::NameSpace);
if (std::ranges::find(m_nameSpaces, identifier->get()) == m_nameSpaces.end()) {
m_nameSpaces.push_back(identifier->get());
}
}
}
}
if (peek(tkn::Literal::Identifier)) {
auto identifier = getValue<Token::Identifier>(0);
Token::Identifier::IdentifierType identifierType;
if (identifier == nullptr) {
next();
continue;
}
identifierType = identifier->getType();
std::string variableName = identifier->get();
if (m_tokenColors.contains(token) && (m_tokenColors.at(token) != ui::TextEditor::PaletteIndex::Default && identifierType != IdentifierType::Unknown)) {
next();
continue;
}
Definition definition;
if (peek(tkn::Keyword::Parent, -2)) {
auto save = m_curr;
next(-2);
while (peek(tkn::Keyword::Parent, -2))
next(-2);
auto optional = setChildrenTypes();
if (optional.has_value())
setIdentifierColor(-1, optional->idType);
else {
m_curr = save;
setIdentifierColor(-1, IdentifierType::Unknown);
next();
continue;
}
m_curr = save;
next();
continue;
} else if (peek(tkn::Operator::ScopeResolution, 1)) {
if (std::ranges::find(m_nameSpaces, variableName) != m_nameSpaces.end()) {
setIdentifierColor(-1, IdentifierType::NameSpace);
next();
continue;
}
} else if (peek(tkn::Operator::ScopeResolution, -1)) {
auto save = m_curr;
next(-2);
if (auto parentIdentifier = const_cast<Identifier *>(getValue<Token::Identifier>(0)); parentIdentifier != nullptr) {
next(2);
if (parentIdentifier->getType() == IdentifierType::UDT) {
auto parentName = parentIdentifier->get();
auto typeName = findIdentifierType(variableName, parentName);
setIdentifierColor(-1, typeName);
}
}
m_curr = save;
next();
continue;
} else if (findIdentifierDefinition(definition)) {
identifierType = definition.idType;
setIdentifierColor(-1, identifierType);
next();
continue;
} else if (std::ranges::find(m_UDTs, variableName) != m_UDTs.end()) {
if (m_typeDefMap.contains(variableName))
setIdentifierColor(-1, IdentifierType::Typedef);
else
setIdentifierColor(-1, IdentifierType::UDT);
next();
continue;
} else if (peek(tkn::Keyword::From, -1)) {
setIdentifierColor(-1, IdentifierType::GlobalVariable);
next();
continue;
} else {
setIdentifierColor(-1, IdentifierType::Unknown);
next();
continue;
}
}
next();
}
}
void TextHighlighter::setRequestedIdentifierColors() {
auto topLine = 0;
auto bottomLine = m_lines.size();
for (u32 line = topLine; line < bottomLine; line = nextLine(line)) {
if (m_lines[line].empty())
continue;
std::string lineOfColors = std::string(m_lines[line].size(), 0);
for (auto tokenIndex = m_firstTokenIdOfLine[line]; tokenIndex < m_firstTokenIdOfLine[nextLine(line)]; tokenIndex++) {
Token *token = const_cast<Token *>(&m_tokens[tokenIndex]);
if (m_tokenColors.contains(token) && token->type == Token::Type::Identifier) {
u8 color = (u8) m_tokenColors.at(token);
u32 tokenLength = token->location.length;
u32 tokenOffset = token->location.column - 1;
if (token->location.line != line + 1)
continue;
if (tokenOffset + tokenLength - 1 >= m_lines[line].size())
continue;
for (u32 j = 0; j < tokenLength; j++)
lineOfColors[tokenOffset + j] = color;
}
}
ui::TextEditor *editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
editor->setColorizedLine(line, lineOfColors);
else
log::warn("Text editor not found, provider is null");
}
}
void TextHighlighter::recurseInheritances(std::string name) {
if (m_inheritances.contains(name)) {
for (auto inheritance: m_inheritances[name]) {
recurseInheritances(inheritance);
auto definitions = m_UDTVariables[inheritance];
if (definitions.empty())
definitions = m_ImportedUDTVariables[inheritance];
for (auto [variableName, variableDefinitions]: definitions) {
auto tokenRange = m_UDTTokenRange[name];
u32 tokenIndex = tokenRange.start;
for (auto token = tokenRange.start; token < tokenRange.end; token++) {
if (auto operatorTkn = std::get_if<Operator>(&m_tokens.at(token).value);
operatorTkn != nullptr && *operatorTkn == Token::Operator::Colon)
tokenIndex = token + 1;
}
for (auto variableDefinition: variableDefinitions) {
variableDefinition.tokenIndex = tokenIndex;
m_UDTVariables[name][variableName].push_back(variableDefinition);
}
}
}
}
}
void TextHighlighter::appendInheritances() {
for (auto [name, inheritances]: m_inheritances)
recurseInheritances(name);
}
// Get the string of the argument type. This works on function arguments and non-type template arguments.
std::string TextHighlighter::getArgumentTypeName(i32 rangeStart, Token delimiter2) {
auto curr = m_curr;
i32 parameterIndex = getArgumentNumber(rangeStart, getTokenId(m_curr->location));
Token delimiter;
std::string typeStr;
if (parameterIndex > 0)
delimiter = tkn::Separator::Comma;
else
delimiter = delimiter2;
while (!peek(delimiter))
next(-1);
skipToken(tkn::Keyword::Reference);
next();
if (peek(tkn::ValueType::Any))
typeStr = Token::getTypeName(*getValue<Token::ValueType>(0));
else if (peek(tkn::Literal::Identifier))
typeStr = getValue<Token::Identifier>(0)->get();
m_curr = curr;
return typeStr;
}
bool TextHighlighter::isTokenIdValid(i32 tokenId) {
return tokenId >= 0 && tokenId < (i32) m_tokens.size();
}
bool TextHighlighter::isLocationValid(hex::plugin::builtin::TextHighlighter::Location location) {
const pl::api::Source *source;
try {
source = location.source;
} catch (const std::out_of_range &e) {
log::error("TextHighlighter::IsLocationValid: Out of range error: {}", e.what());
return false;
}
if (source == nullptr)
return false;
i32 line = location.line - 1;
i32 col = location.column - 1;
i32 length = location.length;
if ( line < 0 || line >= (i32) m_lines.size())
return false;
if ( col < 0 || col > (i32) m_lines[line].size())
return false;
if (length < 0 || length > (i32) m_lines[line].size()-col)
return false;
return true;
}
// Find the string of the variable type. This works on function variables, views,
// local variables as well as on calculated pointers and pattern variables.
std::string TextHighlighter::getVariableTypeName() {
auto curr = m_curr;
auto varTokenId = getTokenId(m_curr->location);
if (!isTokenIdValid(varTokenId))
return "";
std::string typeStr;
skipToken(tkn::Operator::Star, -1);
while (peek(tkn::Separator::Comma, -1))
next(-2);
if (peek(tkn::ValueType::Any, -1))
typeStr = Token::getTypeName(*getValue<Token::ValueType>(-1));
else if (peek(tkn::Keyword::Signed, -1))
typeStr = "signed";
else if (peek(tkn::Keyword::Unsigned, -1))
typeStr = "unsigned";
else {
skipTemplate(200, false);
next(-1);
if (peek(tkn::Literal::Identifier)) {
typeStr = getValue<Token::Identifier>(0)->get();
next(-1);
}
std::string nameSpace;
while (peek(tkn::Operator::ScopeResolution)) {
next(-1);
nameSpace = getValue<Token::Identifier>(0)->get() + "::" + nameSpace;
next(-1);
}
typeStr = nameSpace + typeStr;
using Types = std::map<std::string, pl::hlp::safe_shared_ptr<pl::core::ast::ASTNodeTypeDecl>>;
auto parser = patternLanguage->get()->getInternals().parser.get();
Types types = parser->getTypes();
if (types.contains(typeStr)) {
m_curr = curr;
return typeStr;
}
std::vector<std::string> candidates;
for (auto name: m_UDTs) {
auto vectorString = wolv::util::splitString(name, "::");
if (typeStr == vectorString.back())
candidates.push_back(name);
}
if (candidates.size() == 1) {
m_curr = curr;
return candidates[0];
}
}
m_curr = curr;
return typeStr;
}
// Definitions of global variables and placed variables.
void TextHighlighter::loadGlobalDefinitions(Scopes tokenRangeSet, std::vector<IdentifierType> identifierTypes, Variables &variables) {
m_startToken = m_originalPosition = m_partOriginalPosition = TokenIter(m_tokens.begin(), m_tokens.end());
for (auto range: tokenRangeSet) {
auto startToken = TokenIter(m_tokens.begin()+range.start,m_tokens.begin()+range.end);
auto endToken = TokenIter(m_tokens.begin()+range.end,m_tokens.end());
for ( m_curr = startToken; endToken > m_curr; next()) {
if (peek(tkn::Literal::Identifier)) {
auto identifier = getValue<Token::Identifier>(0);
auto identifierType = identifier->getType();
auto identifierName = identifier->get();
if (std::ranges::find(identifierTypes, identifierType) != identifierTypes.end()) {
std::string typeStr = getVariableTypeName();
if (typeStr.empty())
continue;
i32 tokenId = getTokenId(m_curr->location);
Definition definition(identifierType, typeStr, tokenId, m_curr->location);
variables[identifierName].push_back(definition);
continue;
}
}
}
}
}
// Definitions of variables and arguments in functions and user defined types.
void TextHighlighter::loadVariableDefinitions(UnorderedBlocks tokenRangeMap, Token delimiter1, Token delimiter2,
std::vector<IdentifierType> identifierTypes,
bool isArgument, VariableMap &variableMap) {
for (auto [name, range]: tokenRangeMap) {
m_curr = m_startToken;
auto endToken = m_startToken;
next(range.start);
if (isArgument) {
while (!peek(delimiter1)) {
if (peek(tkn::Separator::LeftBrace))
break;
next();
}
if (peek(tkn::Separator::LeftBrace))
continue;
endToken = m_curr;
while (!peek(delimiter2)) {
if (peek(tkn::Separator::LeftBrace))
break;
next();
}
if (peek(tkn::Separator::LeftBrace))
continue;
auto temp = m_curr;
m_curr = endToken;
endToken = temp;
} else
endToken = endToken + range.end;
Keyword *keyword;
for (keyword = std::get_if<Keyword>(&m_tokens[range.start].value); endToken > m_curr; next()) {
if (peek(tkn::Literal::Identifier)) {
auto identifier = getValue<Token::Identifier>(0);
if (identifier == nullptr)
continue;
auto identifierType = identifier->getType();
auto identifierName = identifier->get();
if (std::ranges::find(identifierTypes, identifierType) != identifierTypes.end()) {
std::string typeStr;
if (keyword != nullptr && (*keyword == Keyword::Enum)) {
typeStr = name;
} else if (isArgument) {
typeStr = getArgumentTypeName(range.start, delimiter1);
} else {
typeStr = getVariableTypeName();
if (typeStr.empty() && keyword != nullptr && *keyword == Keyword::Bitfield)
typeStr = "bits";
if (m_typeDefMap.contains(typeStr))
typeStr = m_typeDefMap[typeStr];
}
if (typeStr.empty())
continue;
Definition definition(identifierType, typeStr, getTokenId(m_curr->location), m_curr->location);
variableMap[name][identifierName].push_back(definition);
continue;
}
}
}
}
}
// Definitions of user defined types and functions.
void TextHighlighter::loadTypeDefinitions( UnorderedBlocks tokenRangeMap, std::vector<IdentifierType> identifierTypes, Definitions &types) {
for (auto [name, range]: tokenRangeMap) {
m_curr = m_startToken + range.start+1;
if (!peek(tkn::Literal::Identifier))
continue;
auto identifier = getValue<Token::Identifier>(0);
if (identifier == nullptr)
continue;
auto identifierType = identifier->getType();
if (std::ranges::find(identifierTypes, identifierType) == identifierTypes.end())
continue;
auto identifierName = identifier->get();
if (!name.ends_with(identifierName))
continue;
types[name] = ParentDefinition(identifierType, getTokenId(m_curr->location), m_curr->location);
}
}
// Once types are loaded from parsed tokens we can create
// maps of variable names to their definitions.
void TextHighlighter::getDefinitions() {
using IdentifierType::LocalVariable;
using IdentifierType::PatternVariable;
using IdentifierType::CalculatedPointer;
using IdentifierType::GlobalVariable;
using IdentifierType::PlacedVariable;
using IdentifierType::View;
using IdentifierType::FunctionVariable;
using IdentifierType::FunctionParameter;
using IdentifierType::TemplateArgument;
using IdentifierType::UDT;
using IdentifierType::Function;
if (!m_UDTDefinitions.empty())
m_UDTDefinitions.clear();
loadTypeDefinitions(m_UDTTokenRange, {UDT}, m_UDTDefinitions);
if (!m_globalVariables.empty())
m_globalVariables.clear();
loadGlobalDefinitions(m_globalTokenRange, {GlobalVariable, PlacedVariable}, m_globalVariables);
if (!m_UDTVariables.empty())
m_UDTVariables.clear();
loadVariableDefinitions(m_UDTTokenRange, tkn::Operator::BoolLessThan, tkn::Operator::BoolGreaterThan,
{TemplateArgument}, true, m_UDTVariables);
loadVariableDefinitions(m_UDTTokenRange, tkn::Operator::BoolLessThan, tkn::Operator::BoolGreaterThan,
{LocalVariable, PatternVariable, CalculatedPointer}, false, m_UDTVariables);
appendInheritances();
if (!m_functionDefinitions.empty())
m_functionDefinitions.clear();
loadTypeDefinitions(m_functionTokenRange, {Function}, m_functionDefinitions);
if (!m_functionVariables.empty())
m_functionVariables.clear();
loadVariableDefinitions(m_functionTokenRange, tkn::Separator::LeftParenthesis, tkn::Separator::RightParenthesis,
{FunctionParameter}, true, m_functionVariables);
loadVariableDefinitions(m_functionTokenRange, tkn::Separator::LeftParenthesis, tkn::Separator::RightParenthesis,
{View,FunctionVariable}, false, m_functionVariables);
}
// Load the source code into the text highlighter, splits
// the text into lines and creates a lookup table for the
// first token id of each line.
void TextHighlighter::loadText() {
if (!m_lines.empty())
m_lines.clear();
if (m_text.empty()) {
ui::TextEditor *editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
m_text = editor->getText();
else
log::warn("Text editor not found, provider is null");
}
m_lines = wolv::util::splitString(m_text, "\n");
m_lines.push_back("");
m_firstTokenIdOfLine.clear();
m_firstTokenIdOfLine.resize(m_lines.size(), -1);
i32 tokenId = 0;
i32 tokenCount = m_tokens.size();
i32 index;
if (tokenCount > 0) {
index = m_tokens[0].location.line - 1;
m_firstTokenIdOfLine[index] = 0;
}
i32 count = m_lines.size();
for (i32 currentLine = 0; currentLine < count; currentLine++) {
for (index = m_tokens[tokenId].location.line - 1; index <= currentLine && tokenId + 1 < tokenCount; tokenId++) {
index = m_tokens[tokenId + 1].location.line - 1;
}
if (index > currentLine) {
m_firstTokenIdOfLine[index] = tokenId;
}
}
if (m_firstTokenIdOfLine.back() != tokenCount)
m_firstTokenIdOfLine.push_back(tokenCount);
}
// Some tokens span many lines and some lines have no tokens. This
// function helps to find the next line number in the inner loop.
u32 TextHighlighter::nextLine(u32 line) {
auto currentTokenId = m_firstTokenIdOfLine[line];
u32 i = 1;
while (line + i < m_lines.size() &&
(m_firstTokenIdOfLine[line + i] == currentTokenId || m_firstTokenIdOfLine[line + i] == (i32) 0xFFFFFFFF))
i++;
return i + line;
}
u32 TextHighlighter::previousLine(u32 line) {
auto currentTokenId = m_firstTokenIdOfLine[line];
u32 i = 1;
while (line - i < m_lines.size() &&
(m_firstTokenIdOfLine[line - i] == currentTokenId || m_firstTokenIdOfLine[line - i] == (i32) 0xFFFFFFFF))
i++;
return line - i;
}
// global token ranges are the complement (aka inverse) of the union
// of the UDT and function token ranges
void TextHighlighter::invertGlobalTokenRange() {
std::set<Interval> ranges;
auto size = m_globalTokenRange.size();
auto tokenCount = m_tokens.size();
if (size == 0) {
ranges.insert(Interval(0, tokenCount));
} else {
auto it = m_globalTokenRange.begin();
auto it2 = std::next(it);
if (it->start != 0)
ranges.insert(Interval(0, it->start));
while (it2 != m_globalTokenRange.end()) {
if (it->end < it2->start)
ranges.insert(Interval(it->end, it2->start));
else
ranges.insert(Interval(it->start, it2->end));
it = it2;
it2 = std::next(it);
}
if (it->end < (i32) (tokenCount-1))
ranges.insert(Interval(it->end, tokenCount-1));
}
m_globalTokenRange = ranges;
}
// 0 for 1st argument, 1 for 2nd argument, etc. Obtained counting commas.
i32 TextHighlighter::getArgumentNumber(i32 start, i32 arg) {
i32 count = 0;
m_curr = m_startToken;
auto endToken = m_startToken + arg;
next(start);
while (endToken > m_curr) {
if (peek(tkn::Separator::Comma))
count++;
next();
}
return count;
}
// The inverse of getArgumentNumber.
void TextHighlighter::getTokenIdForArgument(i32 start, i32 argNumber, Token delimiter) {
m_curr = m_startToken;
next(start);
while (!peek(delimiter))
next();
next();
i32 count = 0;
while (count < argNumber && !peek(tkn::Separator::EndOfProgram)) {
if (peek(tkn::Separator::Comma))
count++;
next();
}
}
// Changes auto type string in definitions to the actual type string.
void TextHighlighter::resolveAutos(VariableMap &variableMap, UnorderedBlocks &tokenRange) {
auto curr = m_curr;
std::string UDTName;
for (auto &[name, variables]: variableMap) {
for (auto &[variableName, definitions]: variables) {
for (auto &definition: definitions) {
if (definition.typeStr == "auto" && (definition.idType == Token::Identifier::IdentifierType::TemplateArgument || definition.idType == Token::Identifier::IdentifierType::FunctionParameter)) {
auto argumentIndex = getArgumentNumber(tokenRange[name].start, definition.tokenIndex);
if (tokenRange == m_UDTTokenRange || !m_attributeFunctionArgumentType.contains(name) ||
m_attributeFunctionArgumentType[name].empty()) {
auto instances = m_instances[name];
for (auto instance: instances) {
if (std::abs(definition.tokenIndex - instance) <= 5)
continue;
Token delimiter;
if (tokenRange == m_UDTTokenRange)
delimiter = tkn::Operator::BoolLessThan;
else
delimiter = tkn::Separator::LeftParenthesis;
std::string fullName;
std::vector<Identifier *> identifiers;
getTokenIdForArgument(instance, argumentIndex,delimiter);
forwardIdentifierName(fullName, identifiers);
if (fullName.starts_with("Parent.")) {
auto fixedDefinition = setChildrenTypes();
if (fixedDefinition.has_value() &&
m_UDTDefinitions.contains(fixedDefinition->typeStr)) {
definition.typeStr = fixedDefinition->typeStr;
continue;
}
} else if (fullName.contains(".")) {
Definition definitionTemp;
resolveIdentifierType(definitionTemp,fullName);
definition.typeStr = definitionTemp.typeStr;
} else {
auto typeName = findIdentifierTypeStr(fullName);
definition.typeStr = typeName;
}
}
} else {
UDTName = m_attributeFunctionArgumentType[name];
if (m_UDTDefinitions.contains(UDTName)) {
definition.typeStr = UDTName;
continue;
}
}
}
}
}
}
m_curr = curr;
}
void TextHighlighter::fixAutos() {
resolveAutos(m_functionVariables, m_functionTokenRange);
resolveAutos(m_UDTVariables, m_UDTTokenRange);
}
void TextHighlighter::fixChains() {
if (!m_scopeChains.empty()) {
for (auto chain: m_scopeChains) {
m_curr = m_startToken + chain;
colorSeparatorScopeChain();
}
}
if (!m_memberChains.empty()) {
for (auto chain: m_memberChains) {
m_curr = m_startToken + chain;
colorOperatorDotChain();
}
}
}
// Calculates the union of all the UDT and function token ranges
// and inverts the result.
void TextHighlighter::getGlobalTokenRanges() {
std::set<Interval> ranges;
for (auto [name, range]: m_UDTTokenRange)
ranges.insert(range);
for (auto [name, range]: m_functionTokenRange)
ranges.insert(range);
if (ranges.empty())
return;
auto it = ranges.begin();
auto next = std::next(it);
while (next != ranges.end()) {
if (next->start - it->end < 2) {
Interval &range = const_cast<Interval &>(*it);
range.end = next->end;
ranges.erase(next);
next = std::next(it);
} else {
it++;
next = std::next(it);
}
}
m_globalTokenRange = ranges;
invertGlobalTokenRange();
for (auto tokenRange: m_globalTokenRange) {
if ((u32) tokenRange.end == m_tokens.size()) {
tokenRange.end -= 1;
m_globalBlocks.insert(tokenRange);
}
}
}
// Parser labels global variables that are not placed as
// function variables.
void TextHighlighter::fixGlobalVariables() {
m_startToken = m_originalPosition = m_partOriginalPosition = TokenIter(m_tokens.begin(), m_tokens.end());
for (auto range: m_globalTokenRange) {
auto startToken = TokenIter(m_tokens.begin() + range.start, m_tokens.begin() + range.end);
auto endToken = TokenIter(m_tokens.begin() + range.end, m_tokens.end());
for (m_curr = startToken; endToken > m_curr; next()) {
if (auto identifier = getValue<Token::Identifier>(0); identifier != nullptr) {
auto identifierType = identifier->getType();
auto identifierName = identifier->get();
if (identifierType == IdentifierType::FunctionVariable) {
identifier->setType(IdentifierType::GlobalVariable, true);
setIdentifierColor(-1, IdentifierType::GlobalVariable);
} else if (identifierType == IdentifierType::View) {
identifier->setType(IdentifierType::PlacedVariable, true);
setIdentifierColor(-1,IdentifierType::PlacedVariable);
} else if (identifierType == IdentifierType::Unknown) {
if (std::ranges::find(m_UDTs,identifierName) != m_UDTs.end()) {
identifier->setType(IdentifierType::UDT, true);
setIdentifierColor(-1, IdentifierType::UDT);
}
}
}
}
}
}
void TextHighlighter::clearVariables() {
if (!m_inheritances.empty())
m_inheritances.clear();
if (!m_globalTokenRange.empty())
m_globalTokenRange.clear();
if (!m_namespaceTokenRange.empty())
m_namespaceTokenRange.clear();
if (!m_UDTDefinitions.empty())
m_UDTDefinitions.clear();
if (!m_UDTBlocks.empty())
m_UDTBlocks.clear();
if (!m_UDTTokenRange.empty())
m_UDTTokenRange.clear();
if (!m_functionDefinitions.empty())
m_functionDefinitions.clear();
if (!m_functionBlocks.empty())
m_functionBlocks.clear();
if (!m_functionTokenRange.empty())
m_functionTokenRange.clear();
if (!m_functionVariables.empty())
m_functionVariables.clear();
if (!m_attributeFunctionArgumentType.empty())
m_attributeFunctionArgumentType.clear();
if (!m_memberChains.empty())
m_memberChains.clear();
if (!m_scopeChains.empty())
m_scopeChains.clear();
}
void TextHighlighter::processSource() {
getAllTokenRanges(IdentifierType::NameSpace);
getAllTokenRanges(IdentifierType::UDT);
getDefinitions();
m_ImportedUDTVariables.insert(m_UDTVariables.begin(), m_UDTVariables.end());
clearVariables();
}
// Only update if needed. Must wait for the parser to finish first.
void TextHighlighter::highlightSourceCode() {
m_wasInterrupted = false;
ON_SCOPE_EXIT {
if (!m_tokenColors.empty())
m_tokenColors.clear();
m_runningColorizers--;
if (m_wasInterrupted) {
m_needsToUpdateColors = true;
m_viewPatternEditor->setChangesWereParsed(true);
} else {
m_needsToUpdateColors = false;
m_viewPatternEditor->setChangesWereParsed(false);
}
};
try {
m_runningColorizers++;
auto preprocessor = patternLanguage->get()->getInternals().preprocessor.get();
auto parser = patternLanguage->get()->getInternals().parser.get();
using Types = std::map<std::string, pl::hlp::safe_shared_ptr<pl::core::ast::ASTNodeTypeDecl>>;
Types types = parser->getTypes();
if (!m_UDTs.empty())
m_UDTs.clear();
for (auto &[name, type]: types)
m_UDTs.push_back(name);
// Namespaces from included files.
m_nameSpaces.clear();
m_nameSpaces = preprocessor->getNamespaces();
clearVariables();
m_parsedImports = preprocessor->getParsedImports();
for (auto &[name, tokens]: m_parsedImports) {
m_tokens = tokens;
m_text = tokens[0].location.source->content;
if (m_text.empty() || m_text == "\n")
return;
loadText();
processSource();
if (!m_tokenColors.empty())
m_tokenColors.clear();
}
m_tokens = preprocessor->getResult();
if (m_tokens.empty())
return;
if (!m_globalTokenRange.empty())
m_globalTokenRange.clear();
m_globalTokenRange.insert(Interval(0, m_tokens.size()-1));
ui::TextEditor *editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
m_text = editor->getText();
else
log::warn("Text editor not found, provider is null");
if (m_text.empty() || m_text == "\n")
return;
loadText();
getAllTokenRanges(IdentifierType::NameSpace);
getAllTokenRanges(IdentifierType::UDT);
getAllTokenRanges(IdentifierType::Function);
getGlobalTokenRanges();
fixGlobalVariables();
setInitialColors();
loadInstances();
getAllTokenRanges(IdentifierType::Attribute);
getDefinitions();
fixAutos();
fixChains();
m_excludedLocations = preprocessor->getExcludedLocations();
colorRemainingIdentifierTokens();
setRequestedIdentifierColors();
editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
editor->clearErrorMarkers();
else
log::warn("Text editor not found, provider is null");
m_compileErrors = patternLanguage->get()->getCompileErrors();
if (!m_compileErrors.empty())
renderErrors();
else {
editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
editor->clearErrorMarkers();
else
log::warn("Text editor not found, provider is null");
}
} catch (const std::out_of_range &e) {
log::debug("TextHighlighter::highlightSourceCode: Out of range error: {}", e.what());
m_wasInterrupted = true;
return;
}
return;
}
}