Files
imhex/plugins/builtin/source/content/text_highlighting/pattern_language.cpp
paxcut 854535fec6 fix: Some variables are incorrectly highlighted as errors (#2690)
Non-auto function arguments of custom types defined inside a namespace
made the highlighter unable to check if member uses inside the function
are valid. Argument types were not parsed as fully qualified types and
the problem is corrected by making sure that fully qualified names are
used when they are found in function arguments.

An example of a pattern that shows the error is id3.hexpat in the which
uses unqualified types. In order to obtain the correct highlighting then
you have to add the namespace to all custom types even if they are being
used inside the namespace. Type names without namespace are considered
by the syntax highlighting code as belonging to the global scope
regardless of where the use takes place.
2026-03-20 10:45:01 -07:00

2565 lines
105 KiB
C++

#include <algorithm>
#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;
using RequiredInputs = TextHighlighter::RequiredInputs;
using Interval = TextHighlighter::Interval;
Interval::Interval(i32 start, i32 end) : start(start), end(end) {
if (start > end)
throw std::invalid_argument("Interval start must be less than or equal to end");
}
bool Interval::operator<(const Interval &other) const {
return other.end > end;
}
bool Interval::operator>(const Interval &other) const {
return end > other.end;
}
bool Interval::operator==(const Interval &other) const {
return start == other.start && end == other.end;
}
bool Interval::operator!=(const Interval &other) const {
return start != other.start || end != other.end;
}
bool Interval::operator<=(const Interval &other) const {
return other.end >= end;
}
bool Interval::operator>=(const Interval &other) const {
return end >= other.end;
}
bool Interval::contains(const Interval &other) const {
return other.start >= start && other.end <= end;
}
bool Interval::contains(i32 value) const {
return value >= start && value <= end;
}
bool Interval::contiguous(const Interval &other) const {
return ((start - other.end) == 1 || (other.start - end) == 1);
}
void TextHighlighter::next(i32 count) {
if (m_viewPatternEditor->interrupted()) {
m_viewPatternEditor->resetInterrupt();
throw std::out_of_range("Highlights were deliberately interrupted");
}
if (count == 0)
return;
i32 id = getTokenId(m_curr->location);
i32 maxChange;
if (count > 0)
maxChange = std::min(count,static_cast<i32>(m_requiredInputs.fullTokens.size() - id));
else
maxChange = -std::min(-count,id);
m_curr += maxChange;
}
pl::PatternLanguage *TextHighlighter::getPatternLanguage() {
return m_viewPatternEditor->getPatternLanguage()->get();
}
void TextHighlighter::RequiredInputs::setTypes() {
auto &types = m_textHighlighter->getPatternLanguage()->getInternals().parser.get()->getTypes();
std::ranges::copy(types.begin(), types.end(), std::inserter(definedTypes, definedTypes.begin()));
}
void TextHighlighter::RequiredInputs::setNamespaces() {
auto &namespaces = m_textHighlighter->getPatternLanguage()->getInternals().preprocessor.get()->getNamespaces();
usedNamespaces.resize(namespaces.size());
std::ranges::copy(namespaces, usedNamespaces.begin());
}
void TextHighlighter::RequiredInputs::setImports() {
auto *preprocessor = m_textHighlighter->getPatternLanguage()->getInternals().preprocessor.get();
std::ranges::copy(preprocessor->getParsedImports().begin(), preprocessor->getParsedImports().end(), std::inserter(parsedImports, parsedImports.begin()));
m_textHighlighter->clearVariables();
for (auto &[name, tokens]: parsedImports) {
importedHeaders[name] = tokens[0].location.source->content;
if (importedHeaders[name].empty() || importedHeaders[name] == "\n")
continue;
fullTokens = tokens;
editedText = importedHeaders[name];
m_textHighlighter->loadText();
m_textHighlighter->processSource();
}
}
void TextHighlighter::RequiredInputs::setText() {
ui::TextEditor *editor = m_textHighlighter->m_viewPatternEditor->getTextEditor();
if (editor == nullptr)
return;
fullTokens.clear();
auto &result = m_textHighlighter->getPatternLanguage()->getInternals().preprocessor.get()->getResult();
std::ranges::copy(result.begin(),result.end(),std::back_inserter(fullTokens));
editedText = editor->getText();
m_textHighlighter->loadText();;
linesOfColors.clear();
for (auto &line : m_textHighlighter->m_lines)
linesOfColors.push_back(std::string(line.size(), ' '));
}
void TextHighlighter::RequiredInputs::setCompileErrors() {
auto errors = m_textHighlighter->getPatternLanguage()->getCompileErrors();
compileErrors.resize(errors.size());
std::ranges::copy(errors, compileErrors.begin());
}
void TextHighlighter::RequiredInputs::setRequiredInputs() {
setTypes();
setNamespaces();
setImports();
setText();
setCompileErrors();
}
void TextHighlighter::updateRequiredInputs() {
m_requiredInputs.m_textHighlighter = this;
m_requiredInputs.setRequiredInputs();
}
RequiredInputs& TextHighlighter::getRequiredInputs() {
return m_requiredInputs;
}
ViewPatternEditor* TextHighlighter::getViewPatternEditor() {
return m_viewPatternEditor;
}
template<typename T>
T *TextHighlighter::getValue(const i32 index) {
return const_cast<T*>(std::get_if<T>(&m_curr[index].value));
}
void TextHighlighter::setViewPatternEditor(ViewPatternEditor *viewPatternEditor) {
m_viewPatternEditor = viewPatternEditor;
}
bool TextHighlighter::begin() {
m_originalPosition = m_curr;
return true;
}
void TextHighlighter::partBegin() {
m_partOriginalPosition = m_curr;
}
void TextHighlighter::reset() {
m_curr = m_originalPosition;
}
void TextHighlighter::partReset() {
m_curr = m_partOriginalPosition;
}
bool TextHighlighter::resetIfFailed(const bool value) {
if (!value) reset();
return value;
}
template<auto S>
bool TextHighlighter::sequenceImpl() {
if constexpr (S == Normal)
return true;
else if constexpr (S == Not)
return false;
else
std::unreachable();
}
template<auto S>
bool TextHighlighter::matchOne(const Token &token) {
if constexpr (S == Normal) {
if (!peek(token)) {
partReset();
return false;
}
next();
return true;
} else if constexpr (S == Not) {
if (!peek(token))
return true;
next();
partReset();
return false;
} else
std::unreachable();
}
template<auto S>
bool TextHighlighter::sequenceImpl(const auto &... args) {
return (matchOne<S>(args) && ...);
}
template<auto S>
bool TextHighlighter::sequence(const Token &token, const auto &... args) {
partBegin();
return sequenceImpl<S>(token, args...);
}
bool TextHighlighter::isValid() {
Token token;
try {
token = m_curr[0];
}
catch (const std::out_of_range &e) {
auto t = e.what();
if (t == nullptr)
return false;
return false;
}
if (!isLocationValid(token.location))
return false;
return true;
}
bool TextHighlighter::peek(const Token &token, const i32 index) {
if (!isValid())
return false;
i32 id = getTokenId(m_curr->location);
if (id+index < 0 || id+index >= (i32)m_requiredInputs.fullTokens.size())
return false;
return m_curr[index].type == token.type && m_curr[index] == token.value;
}
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;
}
TextHighlighter::TextHighlighter() {}
TextHighlighter::~TextHighlighter() {}
// 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;
auto *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 (const auto &name : vectorString) {
found = found || std::ranges::find(m_requiredInputs.usedNamespaces, name) != m_requiredInputs.usedNamespaces.end();
}
if (found) {
if (!shortName.empty())
identifierName = fmt::format("{}::{}", identifierName, shortName);
return true;
}
if (useDefinitions) {
if (m_functionDefinitions.contains(identifierName) || m_UDTDefinitions.contains(identifierName)) {
if (!shortName.empty())
identifierName = fmt::format("{}::{}", identifierName, shortName);
return true;
}
std::string nameSpace;
for (const auto &[name, definition]: m_UDTDefinitions) {
findNamespace(nameSpace, definition.tokenIndex);
if (!nameSpace.empty() && !identifierName.contains(nameSpace)) {
qualifiedName = fmt::format("{}::{}", nameSpace, identifierName);
if (name == qualifiedName) {
identifierName = qualifiedName;
if (!shortName.empty())
identifierName = fmt::format("{}::{}", identifierName, shortName);
return true;
}
}
if (name == identifierName) {
identifierName = name;
if (!shortName.empty())
identifierName = fmt::format("{}::{}", 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, UnorderedBlocks &tokenRange,
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 = fmt::format("{}::{}", nameSpace, name);
}
i32 tokenCount = m_requiredInputs.fullTokens.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 (const auto &keyword: keywords)
result = result && !peek(keyword);
if (result)
return false;
u32 nestedLevel = 0;
next();
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin() + tokenCount, m_requiredInputs.fullTokens.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();
}
if (m_curr > endToken || endToken == m_curr)
return false;
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::getTokenRanges(IdentifierType identifierTypeToSearch) {
if (m_requiredInputs.fullTokens.size() == 1)
return;
Identifier *identifier;
IdentifierType identifierType;
m_startToken = TokenIter(m_requiredInputs.fullTokens.begin(), m_requiredInputs.fullTokens.end());
auto endToken = TokenIter(m_requiredInputs.fullTokens.end(), m_requiredInputs.fullTokens.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();
auto& 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_requiredInputs.usedNamespaces, name) == m_requiredInputs.usedNamespaces.end())
m_requiredInputs.usedNamespaces.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_requiredInputs.fullTokens.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;
}
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_requiredInputs.usedNamespaces, currentName) != m_requiredInputs.usedNamespaces.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.insert(0, nameSpace);
else if (findNamespace(nameSpace) && !variableParentType.contains(nameSpace))
variableParentType = fmt::format("{}::{}", 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_requiredInputs.fullTokens.at(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.empty()) {
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_requiredInputs.fullTokens.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 (const 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::ranges::find_if(definitions, [&](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 (const 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_requiredInputs.fullTokens.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.at(currentLine);
if (startingLineTokenIndex == 0xFFFFFFFFu || startingLineTokenIndex > tokenCount)
return false;
if (auto *keyword = std::get_if<Keyword>(&m_requiredInputs.fullTokens.at(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_requiredInputs.fullTokens.at(instance).value); identifier != nullptr && identifier->getType() == IdentifierType::TemplateArgument) {
auto tokenRange = m_UDTTokenRange[result.typeStr];
auto tokenIndex = m_firstTokenIdOfLine.at(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_requiredInputs.fullTokens.at(instance).value);
identifier != nullptr && identifier->getType() == IdentifierType::Typedef) {
if (auto *identifier2 = std::get_if<Identifier>(&m_requiredInputs.fullTokens.at(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() {
if (!colorRealSeparatorScopeChain())
return colorImplicitSeparatorScopeChain();
return true;
}
bool TextHighlighter::colorImplicitSeparatorScopeChain() {
auto identifier = getValue<Identifier>(0);
if (identifier == nullptr)
return false;
std::string identifierName = identifier->get();
std::string nameSpace;
if (!findNamespace(nameSpace))
return false;
std::string qualifiedName = fmt::format("{}::{}", nameSpace, identifierName);
if (m_UDTDefinitions.contains(qualifiedName)) {
setIdentifierColor(-1, IdentifierType::UDT);
return true;
} else if (m_functionDefinitions.contains(qualifiedName)) {
setIdentifierColor(-1, IdentifierType::Function);
return true;
} else
return false;
}
bool TextHighlighter::colorRealSeparatorScopeChain() {
std::vector<Identifier *> identifiers;
std::string identifierName;
if (!getQualifiedName(identifierName, identifiers, true)) {
return false;
}
auto tokenCount = m_requiredInputs.fullTokens.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_requiredInputs.usedNamespaces, name) != m_requiredInputs.usedNamespaces.end()) {
setIdentifierColor(-1, IdentifierType::NameSpace);
nameSpace += name + "::";
} else if (m_UDTDefinitions.contains(nameSpace+name)) {
name.insert(0, nameSpace);
auto udtDefinition = m_UDTDefinitions[name];
auto definitionIndex = udtDefinition.tokenIndex-1;
if (auto *keyword = std::get_if<Keyword>(&m_requiredInputs.fullTokens.at(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;
const 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_requiredInputs.usedNamespaces, identifierName) != m_requiredInputs.usedNamespaces.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 (const 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.empty())
nameSpace = name;
else
nameSpace = fmt::format("{}::{}", name, nameSpace);
}
}
return !nameSpace.empty();
}
//The context is the name of the function or UDT that the variable is in aka its parent.
std::string TextHighlighter::findIdentifierTypeStr(const std::string &identifierName, std::string context) {
Definition result;
findIdentifierDefinition(result, identifierName, context);
return result.typeStr;
}
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;
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);
} else if (sequence(tkn::Separator::LeftParenthesis, tkn::Literal::Identifier)) {
next(-1);
std::vector<Identifier *> identifiers;
if (!getFullName(functionName, identifiers, false))
functionName = getValue<Identifier>(0)->get();
} else
return;
if (!functionName.contains("::")) {
std::string namespaceName;
if (findNamespace(namespaceName))
functionName = fmt::format("{}::{}", namespaceName, functionName);
} else {
auto vectorString = wolv::util::splitString(functionName, "::");
vectorString.pop_back();
for (const auto &nameSpace: vectorString) {
if (std::ranges::find(m_requiredInputs.usedNamespaces, nameSpace) == m_requiredInputs.usedNamespaces.end())
m_requiredInputs.usedNamespaces.push_back(nameSpace);
}
}
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.at(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 = fmt::format("{}::{}", namespaceName, UDTName);
}
if (m_requiredInputs.definedTypes.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 (const 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 (const auto &[name, variables]: m_UDTVariables) {
for (const auto &[variableName, definitions]: variables) {
for (const 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 (const auto &[name, range] : m_UDTTokenRange) {
auto startToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.start,m_requiredInputs.fullTokens.begin()+range.end);
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.end,m_requiredInputs.fullTokens.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 (const 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 (const 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::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},
};
// 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_requiredInputs.compileErrors.empty()) {
for (const auto &error: m_requiredInputs.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_requiredInputs.fullTokens.begin(), m_requiredInputs.fullTokens.end());
auto endToken = TokenIter(m_requiredInputs.fullTokens.end(), m_requiredInputs.fullTokens.end());
for (m_curr = m_startToken; endToken > m_curr; next()) {
if (peek(tkn::Separator::EndOfProgram))
return;
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);
}
}
}
void TextHighlighter::loadInstances() {
std::map<std::string, std::vector<i32>> instances;
m_startToken = m_originalPosition = m_partOriginalPosition = TokenIter(m_requiredInputs.fullTokens.begin(), m_requiredInputs.fullTokens.end());
auto endToken = TokenIter(m_requiredInputs.fullTokens.end(), m_requiredInputs.fullTokens.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;
//std::string typeName = "";
skipTemplate(200, true);
next();
if (sequence(tkn::Operator::Assign, tkn::Literal::Identifier)) {
next(-1);
i32 fullNameTokenId = getTokenId(m_curr->location);
std::vector<Identifier *> identifiers;
std::string typeName;
if (!getFullName(typeName, identifiers, false)) {
auto identifier2 = getValue<Identifier>(0);
if (identifier2 != nullptr) {
typeName = identifier2->get();
}
} else {
auto count = std::count(typeName.begin(), typeName.end(), ':') >> 1;
for (auto i = 0; i < count; i++) {
setIdentifierColor(fullNameTokenId, IdentifierType::NameSpace);
fullNameTokenId += 2;
}
}
setIdentifierColor(fullNameTokenId, IdentifierType::UDT);
if (!m_typeDefMap.contains(name) && !typeName.empty()) {
m_typeDefMap[name] = typeName;
m_typeDefInvMap[typeName] = name;
}
auto start = m_curr;
skipTemplate(200, true);
auto end = m_curr;
for (m_curr = start; end > m_curr; next()) {
if (auto identifier2 = getValue<Identifier>(0); identifier2 != nullptr) {
auto tokenId = getTokenId(m_curr->location);
setIdentifierColor(tokenId, IdentifierType::TemplateArgument);
}
}
}
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();
std::string nameSpace;
if (!chainStarted) {
chainStarted = true;
m_scopeChains.insert(tokenIndex);
} else if (findNamespace(nameSpace) && !nameSpace.empty()) {
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;
}
if (peek(tkn::Literal::Identifier)) {
std::string nameScape;
if (findNamespace(nameScape) && !nameScape.empty() && std::ranges::find(m_UDTs,(nameScape + "::" + name)) != m_UDTs.end()) {
m_scopeChains.insert(tokenIndex);
}
}
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_requiredInputs.fullTokens.size())
return Location::Empty();
return m_requiredInputs.fullTokens.at(tokenId).location;
}
// Get the token index for a given location.
i32 TextHighlighter::getTokenId(pl::core::Location location) {
if (location == m_requiredInputs.fullTokens.at(0).location)
return 0;
if (location == m_requiredInputs.fullTokens.back().location)
return (i32) m_requiredInputs.fullTokens.size() - 1;
if (!isLocationValid(location))
return -1;
auto line1 = location.line - 1;
auto tokenCount = m_requiredInputs.fullTokens.size();
i32 tokenStart = m_firstTokenIdOfLine.at(line1);
i32 tokenEnd = -1;
if (line1 == m_firstTokenIdOfLine.size() -1) {
tokenEnd = tokenCount - 1;
} else {
auto line2 = nextLine(line1);
tokenEnd = m_firstTokenIdOfLine.at(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_requiredInputs.fullTokens.at(i).location.column >= location.column)
return i;
}
return -1;
}
void TextHighlighter::setIdentifierColor(i32 tokenId, const IdentifierType &type) {
const Token *constToken;
if (tokenId == -1) {
constToken = &m_curr[0];
tokenId = getTokenId(m_curr->location);
} else
constToken = &m_requiredInputs.fullTokens.at(tokenId);
auto token = const_cast<Token *>(constToken);
if (token->type == Token::Type::Identifier && (!m_tokenColors.contains(tokenId) || m_tokenColors.at(tokenId) == ui::TextEditor::PaletteIndex::Default || m_tokenColors.at(tokenId) == ui::TextEditor::PaletteIndex::UnkIdentifier))
m_tokenColors[tokenId] = m_identifierTypeColor.at(type);
}
void TextHighlighter::setColor(i32 tokenId, const IdentifierType &type) {
if (tokenId == -1)
tokenId = getTokenId(m_curr->location);
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_requiredInputs.fullTokens.begin(), m_requiredInputs.fullTokens.end());
auto endToken = TokenIter(m_requiredInputs.fullTokens.end(), m_requiredInputs.fullTokens.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();
}
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_requiredInputs.usedNamespaces, identifier->get()) == m_requiredInputs.usedNamespaces.end()) {
m_requiredInputs.usedNamespaces.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_requiredInputs.usedNamespaces, identifier->get()) == m_requiredInputs.usedNamespaces.end()) {
m_requiredInputs.usedNamespaces.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(tokenId) && (m_tokenColors.at(tokenId) != ui::TextEditor::PaletteIndex::Default && identifierType != IdentifierType::Unknown)) {
next();
continue;
}
Definition definition;
std::string nameSpace;
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;
}
if (peek(tkn::Operator::ScopeResolution, 1)) {
if (std::ranges::find(m_requiredInputs.usedNamespaces, variableName) != m_requiredInputs.usedNamespaces.end()) {
setIdentifierColor(-1, IdentifierType::NameSpace);
next();
continue;
}
}
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;
}
if (findIdentifierDefinition(definition)) {
identifierType = definition.idType;
setIdentifierColor(-1, identifierType);
next();
continue;
}
if (findNamespace(nameSpace,tokenId)) {
auto fullName = nameSpace + "::" + variableName;
auto typeName = findIdentifierType(fullName, "");
if (typeName != IdentifierType::Unknown) {
setIdentifierColor(-1, typeName);
next();
continue;
}
}
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;
}
if (peek(tkn::Keyword::From, -1)) {
setIdentifierColor(-1, IdentifierType::GlobalVariable);
next();
continue;
}
setIdentifierColor(-1, IdentifierType::Unknown);
next();
}
next();
}
}
void TextHighlighter::setRequestedIdentifierColors() {
if (m_tokenColors.empty() || m_firstTokenIdOfLine.empty() || m_requiredInputs.fullTokens.size() < 2)
return;
auto topLine = 0;
while (m_firstTokenIdOfLine.at(topLine) == -1)
topLine++;
auto bottomLine = previousLine(m_firstTokenIdOfLine.size());
for (u32 line = topLine; line < bottomLine; line = nextLine(line)) {
if (m_lines[line].empty())
continue;
std::string &lineOfColors = m_requiredInputs.linesOfColors[line];//std::string(m_lines[line].size(), 0);
for (auto tokenIndex = m_firstTokenIdOfLine.at(line); tokenIndex < m_firstTokenIdOfLine.at(nextLine(line)); tokenIndex++) {
Token *token = const_cast<Token *>(&m_requiredInputs.fullTokens.at(tokenIndex));
if (m_tokenColors.contains(tokenIndex) && token->type == Token::Type::Identifier) {
u8 color = (u8) m_tokenColors.at(tokenIndex);
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 (auto iterator = m_inheritances.find(name); iterator != m_inheritances.end()) {
auto inheritances = std::move(iterator->second);
m_inheritances.erase(iterator);
for (auto inheritance: inheritances) {
recurseInheritances(inheritance);
auto definitions = m_UDTVariables[inheritance];
if (definitions.empty())
definitions = m_ImportedUDTVariables[inheritance];
for (const 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_requiredInputs.fullTokens.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 iterator = m_inheritances.begin(); iterator != m_inheritances.end(); iterator = m_inheritances.begin())
recurseInheritances(iterator->first);
}
// 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)) {
std::vector<Identifier *> identifiers;
getQualifiedName(typeStr, identifiers, true, false);
}
m_curr = curr;
return typeStr;
}
bool TextHighlighter::isTokenIdValid(i32 tokenId) {
return tokenId >= 0 && tokenId < (i32) m_requiredInputs.fullTokens.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.insert(0, "::");
nameSpace.insert(0, getValue<Token::Identifier>(0)->get());
next(-1);
}
typeStr = nameSpace + typeStr;
if (m_requiredInputs.definedTypes.contains(typeStr)) {
m_curr = curr;
return typeStr;
}
std::vector<std::string> candidates;
for (const 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_requiredInputs.fullTokens.begin(), m_requiredInputs.fullTokens.end());
for (auto range: tokenRangeSet) {
auto startToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.start,m_requiredInputs.fullTokens.begin()+range.end);
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.end,m_requiredInputs.fullTokens.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 (const 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_requiredInputs.fullTokens.at(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 (const 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;
m_UDTDefinitions.clear();
loadTypeDefinitions(m_UDTTokenRange, {UDT}, m_UDTDefinitions);
m_globalVariables.clear();
loadGlobalDefinitions(m_globalTokenRange, {GlobalVariable, PlacedVariable}, m_globalVariables);
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();
m_functionDefinitions.clear();
loadTypeDefinitions(m_functionTokenRange, {Function}, m_functionDefinitions);
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_requiredInputs.editedText.empty()) {
ui::TextEditor *editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
m_requiredInputs.editedText = editor->getText();
else
log::warn("Text editor not found, provider is null");
}
m_lines = wolv::util::splitString(m_requiredInputs.editedText, "\n");
m_lines.push_back("");
m_firstTokenIdOfLine.clear();
u32 tokenId = 0;
u32 tokenCount = m_requiredInputs.fullTokens.size();
u32 lineIndex;
u32 count;
if (tokenCount == 0)
return;
lineIndex = m_requiredInputs.fullTokens.at(tokenId).location.line - 1;
count = m_requiredInputs.fullTokens.at(tokenCount - 1).location.line + 1;
m_firstTokenIdOfLine.resize(count, -1);
m_firstTokenIdOfLine.at(lineIndex) = 0;
tokenId++;
u32 currentLine = lineIndex;
while ( currentLine < count) {
while (lineIndex <= currentLine && tokenId <= tokenCount - 1) {
lineIndex = m_requiredInputs.fullTokens.at(tokenId).location.line - 1;
tokenId++;
}
if (tokenId > tokenCount - 1)
break;
if (tokenId > 0)
tokenId--;
if ((m_requiredInputs.fullTokens.at(tokenId).type == pl::core::Token::Type::DocComment || m_requiredInputs.fullTokens.at(tokenId).type == pl::core::Token::Type::Comment)) {
auto *comment = std::get_if<pl::core::Token::Comment>(&(m_requiredInputs.fullTokens.at(tokenId).value));
auto *docComment = std::get_if<pl::core::Token::DocComment>(&(m_requiredInputs.fullTokens.at(tokenId).value));
if ((comment != nullptr && !comment->singleLine) || (docComment != nullptr && !docComment->singleLine)) {
auto commentTokenId = tokenId;
auto commentStartLine = m_requiredInputs.fullTokens.at(tokenId).location.line - 1;
std::string value = m_requiredInputs.fullTokens.at(tokenId).getFormattedValue();
auto commentEndLine = commentStartLine + std::count(value.begin(), value.end(), '\n');
m_firstTokenIdOfLine.at(commentStartLine) = commentTokenId;
for (u32 i = commentStartLine + 1; i <= commentEndLine; i++) {
m_firstTokenIdOfLine.at(i) = -1;
}
lineIndex = commentEndLine;
tokenId++;
} else {
m_firstTokenIdOfLine.at(lineIndex) = tokenId;
tokenId++;
}
} else {
m_firstTokenIdOfLine.at(lineIndex) = tokenId;
tokenId++;
}
currentLine = lineIndex;
}
if (m_firstTokenIdOfLine.back() != (i32) tokenCount - 1)
m_firstTokenIdOfLine.push_back(tokenCount - 1);
}
// Some tokens span many lines and some lines have no tokens. This
// function helps to find the next line number in loops over tokens.
u32 TextHighlighter::nextLine(u32 line) {
if (line >= m_firstTokenIdOfLine.size())
return line;
auto currentTokenId = m_firstTokenIdOfLine.at(line);
u32 i = 1;
while (line + i < m_firstTokenIdOfLine.size() &&
(m_firstTokenIdOfLine.at(line + i) == currentTokenId || m_firstTokenIdOfLine.at(line + i) == (i32) 0xFFFFFFFF))
i++;
return i + line;
}
u32 TextHighlighter::previousLine(u32 line) {
if (line == 0)
return 0;
if (line >= m_firstTokenIdOfLine.size())
return line - 1;
auto currentTokenId = m_firstTokenIdOfLine.at(line);
u32 i = 1;
while (i <= line && line < m_firstTokenIdOfLine.size() &&
(m_firstTokenIdOfLine.at(line - i) == currentTokenId || m_firstTokenIdOfLine.at(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_requiredInputs.fullTokens.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 (const auto &[name, range]: m_UDTTokenRange)
ranges.insert(range);
for (const 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) {
auto &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_requiredInputs.fullTokens.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_requiredInputs.fullTokens.begin(), m_requiredInputs.fullTokens.end());
for (auto range: m_globalTokenRange) {
auto startToken = TokenIter(m_requiredInputs.fullTokens.begin() + range.start, m_requiredInputs.fullTokens.begin() + range.end);
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin() + range.end, m_requiredInputs.fullTokens.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() {
m_inheritances.clear();
m_globalTokenRange.clear();
m_namespaceTokenRange.clear();
m_UDTDefinitions.clear();
m_UDTBlocks.clear();
m_UDTTokenRange.clear();
m_functionDefinitions.clear();
m_functionBlocks.clear();
m_functionTokenRange.clear();
m_functionVariables.clear();
m_attributeFunctionArgumentType.clear();
m_memberChains.clear();
m_scopeChains.clear();
m_tokenColors.clear();
}
void TextHighlighter::processSource() {
m_UDTVariables.clear();
m_UDTTokenRange.clear();
getTokenRanges(IdentifierType::UDT);
loadVariableDefinitions(m_UDTTokenRange, tkn::Operator::BoolLessThan, tkn::Operator::BoolGreaterThan,
{IdentifierType::TemplateArgument}, true, m_UDTVariables);
loadVariableDefinitions(m_UDTTokenRange, tkn::Operator::BoolLessThan, tkn::Operator::BoolGreaterThan,
{IdentifierType::LocalVariable, IdentifierType::PatternVariable, IdentifierType::CalculatedPointer}, false, m_UDTVariables);
m_ImportedUDTVariables.insert(m_UDTVariables.begin(), m_UDTVariables.end());
}
// Only update if needed. Must wait for the parser to finish first.
void TextHighlighter::highlightSourceCode() {
m_viewPatternEditor->resetInterrupt();
ON_SCOPE_EXIT {
m_viewPatternEditor->incrementRunningHighlighters(-1);
m_viewPatternEditor->setChangesWereColored(!m_viewPatternEditor->interrupted());
};
try {
m_viewPatternEditor->incrementRunningHighlighters(1);
clearVariables();
if (!m_UDTs.empty())
m_UDTs.clear();
for (auto &[name, type]: m_requiredInputs.definedTypes)
m_UDTs.push_back(name);
if (!m_globalTokenRange.empty())
m_globalTokenRange.clear();
if (m_requiredInputs.fullTokens.size() > 1) {
m_globalTokenRange.insert(Interval(0, m_requiredInputs.fullTokens.size() - 1));
getTokenRanges(IdentifierType::NameSpace);
getTokenRanges(IdentifierType::UDT);
getTokenRanges(IdentifierType::Function);
getGlobalTokenRanges();
fixGlobalVariables();
setInitialColors();
loadInstances();
getTokenRanges(IdentifierType::Attribute);
getDefinitions();
fixAutos();
fixChains();
colorRemainingIdentifierTokens();
}
ui::TextEditor *editor = m_viewPatternEditor->getTextEditor();
if (editor != nullptr)
editor->clearErrorMarkers();
else
log::warn("Text editor not found, provider is null");
m_requiredInputs.compileErrors = getPatternLanguage()->getCompileErrors();
if (!m_requiredInputs.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());
return;
}
}
}