#include #include #include #include #include #include #include #include #include #include #include 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(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 T *TextHighlighter::getValue(const i32 index) { return const_cast(std::get_if(&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 bool TextHighlighter::sequenceImpl() { if constexpr (S == Normal) return true; else if constexpr (S == Not) return false; else std::unreachable(); } template 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 bool TextHighlighter::sequenceImpl(const auto &... args) { return (matchOne(args) && ...); } template bool TextHighlighter::sequence(const Token &token, const auto &... args) { partBegin(); return sequenceImpl(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(0); identifier = getValue(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 &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 &identifiers, bool preserveCurr ) { auto curr = m_curr; auto *identifier = getValue(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 &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 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 keywords, UnorderedBlocks &tokenRange, OrderedBlocks &tokenRangeInv, bool fullName, VariableScopes *blocks) { bool addArgumentBlock = !fullName; std::vector tokenStack; if (getTokenId(m_curr->location) < 1) return false; std::string name; if (fullName) { std::vector 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 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(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(&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 definitions; std::string name = optionalName; result.idType = IdentifierType::Unknown; std::string identifierName = optionalIdentifierName; if (optionalIdentifierName.empty()) { std::vector 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 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(&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(&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(&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(&m_requiredInputs.fullTokens.at(instance).value); identifier != nullptr && identifier->getType() == IdentifierType::Typedef) { if (auto *identifier2 = std::get_if(&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(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 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(&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(&nextToken.value); auto *operatortk = std::get_if(&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(&previousToken.value); auto *identifier2 = std::get_if(&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(0); if (identifier != nullptr) setIdentifierColor(-1, IdentifierType::Attribute); m_curr = curr; identifier = getValue(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(-1)->toString(false); } else if (sequence(tkn::Separator::LeftParenthesis, tkn::Literal::Identifier)) { next(-1); std::vector identifiers; if (!getFullName(functionName, identifiers, false)) functionName = getValue(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(-2); UDTName += identifier->get() + "::"; } if (sequence(tkn::Literal::Identifier)) { identifier = getValue(-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(-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 &parentTypes, std::vector &identifiers, std::string &optionalFullName) { auto fullName = optionalFullName; if (optionalFullName.empty()) forwardIdentifierName(fullName, identifiers); auto nameParts = wolv::util::splitString(fullName, "."); std::vector 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 &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(&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 &result, std::vector &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 TextHighlighter::setChildrenTypes() { auto curr = m_curr; std::string fullName; std::vector identifiers; std::vector definitions; std::optional result; forwardIdentifierName(fullName, identifiers); std::vector 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(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> 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(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 identifiers; std::string typeName; if (!getFullName(typeName, identifiers, false)) { auto identifier2 = getValue(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(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(-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(-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(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 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(getValue(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(getValue(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(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(getValue(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(&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(&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(0)); else if (peek(tkn::Literal::Identifier)) { std::vector 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(-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(0)->get(); next(-1); } std::string nameSpace; while (peek(tkn::Operator::ScopeResolution)) { next(-1); nameSpace.insert(0, "::"); nameSpace.insert(0, getValue(0)->get()); next(-1); } typeStr = nameSpace + typeStr; if (m_requiredInputs.definedTypes.contains(typeStr)) { m_curr = curr; return typeStr; } std::vector 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 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(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 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(&m_requiredInputs.fullTokens.at(range.start).value); endToken > m_curr; next()) { if (peek(tkn::Literal::Identifier)) { auto identifier = getValue(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 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(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(&(m_requiredInputs.fullTokens.at(tokenId).value)); auto *docComment = std::get_if(&(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 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 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 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(*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(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; } } }