mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-27 23:37:05 -05:00
Code fold support for pattern editor that mimics Clion's but with additional features
- Supports open/close delimiters {} () [] <> and keywords import, #include, #ifdef/#ifndef
- Supports block comments (doc or not) and consecutive single line comments.
- Closed folds are retained when exiting and reopening ImHex (saved in pattern file itself)
- Folds can be chained together by closing and opening successive folds on the same line (only for delimited folds)
- Supports all styles for delimiter placing and allows comments between head and opening delimiter.
- Tooltip shows unfolded code when ellipsis are hovered.
- line numbers update on folded line navigation.
- Line+Column numbers displayed on tooltip by hovering line number field and pressing the shift key.
- Selections and breakpoints rendered on fold sections.
- Vertical line on left margin for matching delimiter visualization.
- Intuitive fold indicators inspired by Clion's editor code folds.
- Fold indicators change dynamically and highlight on hovering.
- Shortcuts to open/close single/all folds or recursively using same keys as Clion's editor.
- Folds open automatically on edits, find/replace, and breakpoints hit.
- Folds also open by clicking on ellipsis.
- Entirely original source code with no third party dependencies.
Not implemented:
- Custom folds based on selection. There is no syntax in pattern language to define independent blocks
Other fixes/changes/improvements
- Improved line number background color for better visibility.
This commit is contained in:
2
dist/ImHex.run.xml
vendored
2
dist/ImHex.run.xml
vendored
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ImHex" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentBuildDir$" PASS_PARENT_ENVS_2="true" PROJECT_NAME="ImHex" TARGET_NAME="imhex_all" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="ImHex" RUN_TARGET_NAME="main">
|
||||
<configuration default="false" name="ImHex" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentBuildDir$" PASS_PARENT_ENVS_2="true" PROJECT_NAME="ImHex" TARGET_NAME="imhex_all" CONFIG_NAME="Debug-MinGW" RUN_TARGET_PROJECT_NAME="ImHex" RUN_TARGET_NAME="main">
|
||||
<envs>
|
||||
<env name="NO_DEBUG_BANNER" value="1" />
|
||||
</envs>
|
||||
|
||||
@@ -1,26 +1,79 @@
|
||||
#pragma once
|
||||
#include <pl/core/token.hpp>
|
||||
#include <pl/core/preprocessor.hpp>
|
||||
#include <pl/pattern_language.hpp>
|
||||
#include <pl/helpers/safe_iterator.hpp>
|
||||
#include <ui/text_editor.hpp>
|
||||
#include <hex/helpers/types.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
class ViewPatternEditor;
|
||||
class TextHighlighter {
|
||||
public:
|
||||
class Interval;
|
||||
/// Intervals are the sets finite contiguous non-negative integer that
|
||||
/// are described by their endpoints. The sets must have the following
|
||||
/// properties:
|
||||
/// 1. Any two elements of the set can either have an empty intersection or
|
||||
/// 2. their intersection is equal to one of the two sets (i.e. one is
|
||||
/// a subset of the other).
|
||||
/// An interval is defined to be smaller than another if:
|
||||
/// 1. The interval lies entirely to the left of the other interval or
|
||||
/// 2. The interval is a proper subset of the other interval.
|
||||
/// Two intervals are equal if they have identical start and end values.
|
||||
/// This ordering is used for things like code blocks or the token
|
||||
/// ranges that are defined by the blocks.
|
||||
|
||||
class TokenInterval {
|
||||
public:
|
||||
friend class TextHighlighter;
|
||||
TokenInterval() : m_start(0), m_end(0) {}
|
||||
TokenInterval(i32 start, i32 end) : m_start(start), m_end(end) {
|
||||
if (m_start > m_end)
|
||||
std::swap(m_start,m_end);
|
||||
}
|
||||
bool operator<(const TokenInterval &other) const {
|
||||
return other.m_end > m_end;
|
||||
}
|
||||
bool operator>(const TokenInterval &other) const {
|
||||
return m_end > other.m_end;
|
||||
}
|
||||
bool operator==(const TokenInterval &other) const {
|
||||
return m_start == other.m_start && m_end == other.m_end;
|
||||
}
|
||||
bool operator!=(const TokenInterval &other) const {
|
||||
return m_start != other.m_start || m_end != other.m_end;
|
||||
}
|
||||
bool operator<=(const TokenInterval &other) const {
|
||||
return other.m_end >= m_end;
|
||||
}
|
||||
bool operator>=(const TokenInterval &other) const {
|
||||
return m_end >= other.m_end;
|
||||
}
|
||||
bool contains(const TokenInterval &other) const {
|
||||
return other.m_start >= m_start && other.m_end <= m_end;
|
||||
}
|
||||
bool contains(i32 value) const {
|
||||
return value >= m_start && value <= m_end;
|
||||
}
|
||||
bool contiguous(const TokenInterval &other) const {
|
||||
auto highEndDiff = m_start - other.m_end;
|
||||
auto lowEndDiff = other.m_start - m_end;
|
||||
return highEndDiff == 0 || highEndDiff == 1 || lowEndDiff == 0 || lowEndDiff == 1;
|
||||
}
|
||||
private:
|
||||
i32 m_start;
|
||||
i32 m_end;
|
||||
};
|
||||
|
||||
using Coordinates = ui::TextEditor::Coordinates;
|
||||
using TokenInterval = TextHighlighter::TokenInterval;
|
||||
using Token = pl::core::Token;
|
||||
using ASTNode = pl::core::ast::ASTNode;
|
||||
using CompileError = pl::core::err::CompileError;
|
||||
using Identifier = Token::Identifier;
|
||||
using IdentifierType = Identifier::IdentifierType;
|
||||
using UnorderedBlocks = std::map<std::string,Interval>;
|
||||
using OrderedBlocks = std::map<Interval,std::string>;
|
||||
using Scopes = std::set<Interval>;
|
||||
using UnorderedBlocks = std::map<std::string,TokenInterval>;
|
||||
using OrderedBlocks = std::map<TokenInterval,std::string>;
|
||||
using Scopes = std::set<TokenInterval>;
|
||||
using Location = pl::core::Location;
|
||||
using VectorString = std::vector<std::string>;
|
||||
using TokenIter = pl::hlp::SafeIterator<std::vector<Token>::const_iterator>;
|
||||
@@ -36,6 +89,7 @@ namespace hex::plugin::builtin {
|
||||
using TokenSequence = std::vector<Token>;
|
||||
using TokenIdVector = std::vector<i32>;
|
||||
using Instances = std::map<std::string,std::vector<i32>>;
|
||||
using CodeFoldBlocks = ui::TextEditor::CodeFoldBlocks;
|
||||
|
||||
struct ParentDefinition;
|
||||
struct Definition {
|
||||
@@ -83,6 +137,7 @@ namespace hex::plugin::builtin {
|
||||
using Variables = std::map<std::string,std::vector<Definition>>;
|
||||
/// to define UDT and function variables
|
||||
using VariableMap = std::map<std::string,Variables>;
|
||||
inline static const Coordinates Invalid = Coordinates(0x80000000, 0x80000000);
|
||||
private:
|
||||
|
||||
VectorString m_lines;
|
||||
@@ -123,43 +178,20 @@ namespace hex::plugin::builtin {
|
||||
VariableScopes m_UDTBlocks;
|
||||
VariableScopes m_functionBlocks;
|
||||
Scopes m_globalBlocks;
|
||||
CodeFoldBlocks m_foldEndPoints;
|
||||
Inheritances m_inheritances;
|
||||
const static IdentifierTypeColor m_identifierTypeColor;
|
||||
const static TokenTypeColor m_tokenTypeColor;
|
||||
|
||||
public:
|
||||
|
||||
/// Intervals are the sets finite contiguous non-negative integer that
|
||||
/// are described by their endpoints. The sets must have the following
|
||||
/// properties:
|
||||
/// 1. Any two elements of the set can either have an empty intersection or
|
||||
/// 2. their intersection is equal to one of the two sets (i.e. one is
|
||||
/// a subset of the other).
|
||||
/// An interval is defined to be smaller than another if:
|
||||
/// 1. The interval lies entirely to the left of the other interval or
|
||||
/// 2. The interval is a proper subset of the other interval.
|
||||
/// Two intervals are equal if they have identical start and end values.
|
||||
/// This ordering is used for things like code blocks or the token
|
||||
/// ranges that are defined by the blocks.
|
||||
class Interval {
|
||||
public:
|
||||
i32 start;
|
||||
i32 end;
|
||||
Interval() : start(0), end(0) {}
|
||||
Interval(i32 start, i32 end);
|
||||
bool operator<(const Interval &other) const;
|
||||
bool operator>(const Interval &other) const;
|
||||
bool operator==(const Interval &other) const;
|
||||
bool operator!=(const Interval &other) const;
|
||||
bool operator<=(const Interval &other) const;
|
||||
bool operator>=(const Interval &other) const;
|
||||
bool contains(const Interval &other) const;
|
||||
bool contains(i32 value) const ;
|
||||
bool contiguous(const Interval &other) const;
|
||||
};
|
||||
constexpr static u32 Normal = 0;
|
||||
constexpr static u32 Not = 1;
|
||||
|
||||
//std::atomic<bool> m_needsToUpdateColors = true;
|
||||
//std::atomic<bool> m_wasInterrupted = false;
|
||||
//std::atomic<bool> m_interrupt = false;
|
||||
//std::atomic<bool> m_completed = false;
|
||||
|
||||
pl::PatternLanguage *getPatternLanguage();
|
||||
void updateRequiredInputs();
|
||||
RequiredInputs& getRequiredInputs();
|
||||
@@ -199,9 +231,11 @@ namespace hex::plugin::builtin {
|
||||
* @brief Renders compile errors in real time
|
||||
*/
|
||||
void renderErrors();
|
||||
|
||||
/// A token range is the set of token indices of a definition. The namespace token
|
||||
/// ranges are obtained first because they are needed to obtain unique identifiers.
|
||||
void getTokenRanges(IdentifierType identifierTypeToSearch);
|
||||
std::vector<TokenInterval> searchRangeForBlocks(TokenInterval interval);
|
||||
/// The global scope is the complement of the union of all the function and UDT token ranges
|
||||
void getGlobalTokenRanges();
|
||||
/// If the current token is a function or UDT, creates a map entry from the name to the token range. These are ordered alphabetically by name.
|
||||
@@ -253,7 +287,7 @@ namespace hex::plugin::builtin {
|
||||
/// If context is empty search for the variable, if it isnt use the variable map.
|
||||
bool findOrContains(std::string &context, UnorderedBlocks tokenRange, VariableMap variableMap);
|
||||
/// Search for instances inside some block
|
||||
void setBlockInstancesColor(const std::string &name, const Definition &definition, const Interval &block);
|
||||
void setBlockInstancesColor(const std::string &name, const Definition &definition, const TokenInterval &block);
|
||||
/// Convenience functions.
|
||||
void skipAttribute();
|
||||
void skipArray(i32 maxSkipCount, bool forward = true);
|
||||
|
||||
@@ -769,7 +769,6 @@
|
||||
"hex.builtin.view.bookmarks.tooltip.open_in_view": "Open in new View",
|
||||
"hex.builtin.view.bookmarks.tooltip.unlock": "Unlock",
|
||||
"hex.builtin.view.command_palette.name": "Command Palette",
|
||||
"hex.builtin.view.find.constants": "Constants",
|
||||
"hex.builtin.view.data_inspector.menu.copy": "Copy Value",
|
||||
"hex.builtin.view.data_inspector.menu.edit": "Edit Value",
|
||||
"hex.builtin.view.data_inspector.execution_error": "Custom row evaluation error",
|
||||
@@ -1125,6 +1124,12 @@
|
||||
"hex.builtin.view.pattern_editor.shortcut.move_bottom": "Move Cursor to the End of the File",
|
||||
"hex.builtin.view.pattern_editor.shortcut.delete_word_left": "Delete One Word to the Left of the Cursor",
|
||||
"hex.builtin.view.pattern_editor.shortcut.delete_word_right": "Delete One Word to the Right of the Cursor",
|
||||
"hex.builtin.view.pattern_editor.shortcut.code_fold_expand": "Expand Code Fold at Cursor",
|
||||
"hex.builtin.view.pattern_editor.shortcut.code_fold_expand_recursively": "Recursively Expand Code Fold at Cursor",
|
||||
"hex.builtin.view.pattern_editor.shortcut.code_fold_expand_all": "Expand All Code Folds",
|
||||
"hex.builtin.view.pattern_editor.shortcut.code_fold_collapse": "Collapse Code Fold at Cursor",
|
||||
"hex.builtin.view.pattern_editor.shortcut.code_fold_collapse_recursively": "Recursively Collapse Code Fold at Cursor",
|
||||
"hex.builtin.view.pattern_editor.shortcut.code_fold_collapse_all": "Collapse All Code Folds",
|
||||
"hex.builtin.view.pattern_editor.menu.edit.run_pattern": "Run Pattern",
|
||||
"hex.builtin.view.pattern_editor.menu.edit.step_debugger": "Step Debugger",
|
||||
"hex.builtin.view.pattern_editor.menu.edit.continue_debugger": "Continue Debugger",
|
||||
|
||||
@@ -11,61 +11,21 @@
|
||||
#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);
|
||||
}
|
||||
using Identifier = Token::Identifier;
|
||||
using Keyword = Token::Keyword;
|
||||
using Directive = Token::Directive;
|
||||
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 TokenInterval = TextHighlighter::TokenInterval;
|
||||
using CodeFoldBlocks = TextHighlighter::CodeFoldBlocks;
|
||||
using Coordinates = TextHighlighter::Coordinates;
|
||||
|
||||
void TextHighlighter::next(i32 count) {
|
||||
if (m_viewPatternEditor->interrupted()) {
|
||||
@@ -84,7 +44,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
pl::PatternLanguage *TextHighlighter::getPatternLanguage() {
|
||||
return m_viewPatternEditor->getPatternLanguage()->get();
|
||||
return m_viewPatternEditor->getPatternLanguage()->get();
|
||||
}
|
||||
|
||||
void TextHighlighter::RequiredInputs::setTypes() {
|
||||
@@ -147,7 +107,7 @@ namespace hex::plugin::builtin {
|
||||
m_requiredInputs.setRequiredInputs();
|
||||
}
|
||||
|
||||
RequiredInputs& TextHighlighter::getRequiredInputs() {
|
||||
TextHighlighter::RequiredInputs& TextHighlighter::getRequiredInputs() {
|
||||
return m_requiredInputs;
|
||||
}
|
||||
|
||||
@@ -405,12 +365,46 @@ namespace hex::plugin::builtin {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<TokenInterval> TextHighlighter::searchRangeForBlocks(TokenInterval interval) {
|
||||
m_curr = m_startToken + interval.m_start;
|
||||
std::vector<TokenInterval> result;
|
||||
|
||||
u32 nestedLevel = 0;
|
||||
std::vector<i32> tokenStack;
|
||||
while (m_curr != m_startToken + interval.m_end) {
|
||||
|
||||
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 result;
|
||||
TokenInterval range(tokenStack.back(), getTokenId(m_curr[-1].location));
|
||||
tokenStack.pop_back();
|
||||
|
||||
result.push_back(range);
|
||||
|
||||
if (nestedLevel == 0) {
|
||||
skipAttribute();
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (peek(tkn::Separator::EndOfProgram))
|
||||
return result;
|
||||
else
|
||||
next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -454,53 +448,27 @@ namespace hex::plugin::builtin {
|
||||
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) {
|
||||
std::vector<TokenInterval> blocksFound = searchRangeForBlocks(TokenInterval(index1, tokenCount-1));
|
||||
if (blocks != nullptr)
|
||||
for (auto range: blocksFound)
|
||||
blocks->operator[](name).insert(range);
|
||||
//if (peek(tkn::Separator::EndOfProgram),-1)
|
||||
// return false;
|
||||
|
||||
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;
|
||||
tokenRangeInv[TokenInterval(index1, index2)] = name;
|
||||
} else {
|
||||
tokenRange[name] = Interval(index1, index2);
|
||||
tokenRange[name] = TokenInterval(index1, index2);
|
||||
}
|
||||
if (blocks != nullptr) {
|
||||
if (addArgumentBlock) {
|
||||
auto tokenIndex = blocks->operator[](name).begin()->start;
|
||||
blocks->operator[](name).insert(Interval(index1, tokenIndex));
|
||||
auto tokenIndex = blocks->operator[](name).begin()->m_start;
|
||||
blocks->operator[](name).insert(TokenInterval(index1, tokenIndex));
|
||||
}
|
||||
blocks->operator[](name).insert(Interval(index1, index2));
|
||||
blocks->operator[](name).insert(TokenInterval(index1, index2));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -703,7 +671,7 @@ namespace hex::plugin::builtin {
|
||||
variableParentType = currentName;
|
||||
|
||||
if (!nameSpace.empty() && !variableParentType.contains(nameSpace))
|
||||
variableParentType.insert(0, nameSpace);
|
||||
variableParentType = nameSpace + variableParentType;
|
||||
|
||||
else if (findNamespace(nameSpace) && !variableParentType.contains(nameSpace))
|
||||
variableParentType = fmt::format("{}::{}", nameSpace, variableParentType);
|
||||
@@ -735,7 +703,7 @@ namespace hex::plugin::builtin {
|
||||
return variableMap.contains(context);
|
||||
}
|
||||
|
||||
void TextHighlighter::setBlockInstancesColor(const std::string &name, const Definition &definition, const Interval &block) {
|
||||
void TextHighlighter::setBlockInstancesColor(const std::string &name, const Definition &definition, const TokenInterval &block) {
|
||||
|
||||
if (definition.idType == IdentifierType::Unknown)
|
||||
return;
|
||||
@@ -762,7 +730,7 @@ namespace hex::plugin::builtin {
|
||||
std::vector<Identifier *> identifiers;
|
||||
getFullName(identifierName, identifiers);
|
||||
}
|
||||
Interval tokenRange;
|
||||
TokenInterval tokenRange;
|
||||
Scopes blocks;
|
||||
Scopes::iterator blocksIterBegin, blocksIterEnd;
|
||||
|
||||
@@ -780,9 +748,10 @@ namespace hex::plugin::builtin {
|
||||
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());
|
||||
tokenRange = TokenInterval(0, m_requiredInputs.fullTokens.size());
|
||||
blocks.insert(tokenRange);
|
||||
blocksIterBegin = blocks.begin();
|
||||
blocksIterEnd = blocks.end();
|
||||
@@ -795,7 +764,7 @@ namespace hex::plugin::builtin {
|
||||
if (isFunction) {
|
||||
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
|
||||
|
||||
if (tokenId > block->start && tokenId < block->end) {
|
||||
if (tokenId > block->m_start && tokenId < block->m_end) {
|
||||
blocksIterBegin = block;
|
||||
break;
|
||||
}
|
||||
@@ -803,7 +772,7 @@ namespace hex::plugin::builtin {
|
||||
for (const auto &definition : definitions) {
|
||||
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
|
||||
|
||||
if (definition.tokenIndex > block->start && definition.tokenIndex < block->end) {
|
||||
if (definition.tokenIndex > block->m_start && definition.tokenIndex < block->m_end) {
|
||||
result = definition;
|
||||
m_curr = curr;
|
||||
|
||||
@@ -814,7 +783,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
auto it = std::ranges::find_if(definitions, [&](const Definition &definition) {
|
||||
return definition.tokenIndex > tokenRange.start && definition.tokenIndex < tokenRange.end;
|
||||
return definition.tokenIndex > tokenRange.m_start && definition.tokenIndex < tokenRange.m_end;
|
||||
});
|
||||
|
||||
if (it != definitions.end()) {
|
||||
@@ -828,7 +797,7 @@ namespace hex::plugin::builtin {
|
||||
} else {
|
||||
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
|
||||
|
||||
if (tokenId > block->start && tokenId < block->end) {
|
||||
if (tokenId > block->m_start && tokenId < block->m_end) {
|
||||
blocksIterBegin = block;
|
||||
break;
|
||||
}
|
||||
@@ -836,7 +805,7 @@ namespace hex::plugin::builtin {
|
||||
for (auto block = blocksIterBegin; block != blocksIterEnd; block++) {
|
||||
for (const auto &definition: definitions) {
|
||||
|
||||
if (definition.tokenIndex > block->start && definition.tokenIndex < block->end) {
|
||||
if (definition.tokenIndex > block->m_start && definition.tokenIndex < block->m_end) {
|
||||
result = definition;
|
||||
m_curr = curr;
|
||||
|
||||
@@ -921,7 +890,7 @@ namespace hex::plugin::builtin {
|
||||
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);
|
||||
i32 argNumber = getArgumentNumber(tokenRange.m_start, instance);
|
||||
getTokenIdForArgument(tokenIndex, argNumber, tkn::Operator::BoolLessThan);
|
||||
if (auto *identifier2 = std::get_if<Identifier>(&m_curr->value); identifier2 != nullptr) {
|
||||
templateName = identifier2->get();
|
||||
@@ -1118,7 +1087,7 @@ namespace hex::plugin::builtin {
|
||||
for (auto [interval, name]: m_namespaceTokenRange) {
|
||||
i32 tokenId = optionalTokenId == -1 ? getTokenId(m_curr->location) : optionalTokenId;
|
||||
|
||||
if (tokenId > interval.start && tokenId < interval.end) {
|
||||
if (tokenId > interval.m_start && tokenId < interval.m_end) {
|
||||
|
||||
if (nameSpace.empty())
|
||||
nameSpace = name;
|
||||
@@ -1319,8 +1288,8 @@ namespace hex::plugin::builtin {
|
||||
} 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());
|
||||
auto startToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.m_start,m_requiredInputs.fullTokens.begin()+range.m_end);
|
||||
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.m_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) {
|
||||
@@ -1645,23 +1614,18 @@ namespace hex::plugin::builtin {
|
||||
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;
|
||||
auto lineCount = m_firstTokenIdOfLine.size();
|
||||
if (line1 >= lineCount || tokenCount == 0)
|
||||
return -1;
|
||||
auto line2 = nextLine(line1);
|
||||
i32 tokenStart = m_firstTokenIdOfLine[line1];
|
||||
i32 tokenEnd = tokenCount - 1;
|
||||
|
||||
if (line2 < lineCount)
|
||||
tokenEnd = m_firstTokenIdOfLine[line2] - 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;
|
||||
}
|
||||
@@ -1681,7 +1645,6 @@ namespace hex::plugin::builtin {
|
||||
|
||||
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) {
|
||||
@@ -1722,6 +1685,8 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
} while (sequence(tkn::Literal::Identifier,tkn::Separator::Dot));
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
next();
|
||||
if (sequence(tkn::Keyword::As, tkn::Literal::Identifier)) {
|
||||
next(-1);
|
||||
@@ -1733,6 +1698,8 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
if (peek(tkn::Literal::Identifier)) {
|
||||
auto identifier = getValue<Token::Identifier>(0);
|
||||
Token::Identifier::IdentifierType identifierType;
|
||||
@@ -1869,8 +1836,8 @@ namespace hex::plugin::builtin {
|
||||
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++) {
|
||||
u32 tokenIndex = tokenRange.m_start;
|
||||
for (auto token = tokenRange.m_start; token < tokenRange.m_end; token++) {
|
||||
|
||||
if (auto operatorTkn = std::get_if<Operator>(&m_requiredInputs.fullTokens.at(token).value);
|
||||
operatorTkn != nullptr && *operatorTkn == Token::Operator::Colon)
|
||||
@@ -1941,8 +1908,17 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if ( col < 0 || col > (i32) m_lines[line].size())
|
||||
return false;
|
||||
if (length < 0)
|
||||
return false;
|
||||
if (length > (i32) m_lines[line].size()-col)
|
||||
length -= (i32)( m_lines[line].size()-col);
|
||||
while (m_firstTokenIdOfLine[line] == m_firstTokenIdOfLine[line + 1]) {
|
||||
length -= (i32) m_lines[line].size();
|
||||
line++;
|
||||
}
|
||||
|
||||
if (length < 0 || length > (i32) m_lines[line].size()-col)
|
||||
|
||||
if (length > (i32) m_lines[line].size()-col && m_firstTokenIdOfLine[line + 1] != -1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -2010,8 +1986,8 @@ namespace hex::plugin::builtin {
|
||||
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());
|
||||
auto startToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.m_start,m_requiredInputs.fullTokens.begin()+range.m_end);
|
||||
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin()+range.m_end,m_requiredInputs.fullTokens.end());
|
||||
|
||||
for ( m_curr = startToken; endToken > m_curr; next()) {
|
||||
|
||||
@@ -2042,11 +2018,12 @@ namespace hex::plugin::builtin {
|
||||
for (const auto &[name, range]: tokenRangeMap) {
|
||||
m_curr = m_startToken;
|
||||
auto endToken = m_startToken;
|
||||
next(range.start);
|
||||
next(range.m_start);
|
||||
|
||||
if (isArgument) {
|
||||
while (!peek(delimiter1)) {
|
||||
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
if (peek(tkn::Separator::LeftBrace))
|
||||
break;
|
||||
next();
|
||||
@@ -2056,7 +2033,8 @@ namespace hex::plugin::builtin {
|
||||
continue;
|
||||
endToken = m_curr;
|
||||
while (!peek(delimiter2)) {
|
||||
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
if (peek(tkn::Separator::LeftBrace))
|
||||
break;
|
||||
next();
|
||||
@@ -2068,11 +2046,12 @@ namespace hex::plugin::builtin {
|
||||
m_curr = endToken;
|
||||
endToken = temp;
|
||||
} else
|
||||
endToken = endToken + range.end;
|
||||
endToken = endToken + range.m_end;
|
||||
|
||||
Keyword *keyword;
|
||||
for (keyword = std::get_if<Keyword>(&m_requiredInputs.fullTokens.at(range.start).value); endToken > m_curr; next()) {
|
||||
|
||||
for (keyword = std::get_if<Keyword>(&m_requiredInputs.fullTokens.at(range.m_start).value); endToken > m_curr; next()) {
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
if (peek(tkn::Literal::Identifier)) {
|
||||
auto identifier = getValue<Token::Identifier>(0);
|
||||
|
||||
@@ -2087,7 +2066,7 @@ namespace hex::plugin::builtin {
|
||||
if (keyword != nullptr && (*keyword == Keyword::Enum)) {
|
||||
typeStr = name;
|
||||
} else if (isArgument) {
|
||||
typeStr = getArgumentTypeName(range.start, delimiter1);
|
||||
typeStr = getArgumentTypeName(range.m_start, delimiter1);
|
||||
} else {
|
||||
typeStr = getVariableTypeName();
|
||||
if (typeStr.empty() && keyword != nullptr && *keyword == Keyword::Bitfield)
|
||||
@@ -2111,7 +2090,7 @@ namespace hex::plugin::builtin {
|
||||
void TextHighlighter::loadTypeDefinitions( UnorderedBlocks tokenRangeMap, std::vector<IdentifierType> identifierTypes, Definitions &types) {
|
||||
for (const auto &[name, range]: tokenRangeMap) {
|
||||
|
||||
m_curr = m_startToken + range.start+1;
|
||||
m_curr = m_startToken + range.m_start+1;
|
||||
|
||||
if (!peek(tkn::Literal::Identifier))
|
||||
continue;
|
||||
@@ -2172,6 +2151,9 @@ namespace hex::plugin::builtin {
|
||||
// the text into lines and creates a lookup table for the
|
||||
// first token id of each line.
|
||||
void TextHighlighter::loadText() {
|
||||
u32 tokenCount = m_requiredInputs.fullTokens.size();
|
||||
if (tokenCount == 0)
|
||||
return;
|
||||
|
||||
if (!m_lines.empty())
|
||||
m_lines.clear();
|
||||
@@ -2188,22 +2170,15 @@ namespace hex::plugin::builtin {
|
||||
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;
|
||||
u32 lineIndex = m_requiredInputs.fullTokens.at(tokenId).location.line - 1;
|
||||
u32 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) {
|
||||
while (lineIndex <= currentLine && tokenId + 1 <= tokenCount) {
|
||||
lineIndex = m_requiredInputs.fullTokens.at(tokenId).location.line - 1;
|
||||
tokenId++;
|
||||
}
|
||||
@@ -2269,29 +2244,29 @@ namespace hex::plugin::builtin {
|
||||
// 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;
|
||||
std::set<TokenInterval> ranges;
|
||||
auto size = m_globalTokenRange.size();
|
||||
auto tokenCount = m_requiredInputs.fullTokens.size();
|
||||
|
||||
if (size == 0) {
|
||||
ranges.insert(Interval(0, tokenCount));
|
||||
ranges.insert(TokenInterval(0, tokenCount));
|
||||
} else {
|
||||
auto it = m_globalTokenRange.begin();
|
||||
auto it2 = std::next(it);
|
||||
if (it->start != 0)
|
||||
ranges.insert(Interval(0, it->start));
|
||||
if (it->m_start != 0)
|
||||
ranges.insert(TokenInterval(0, it->m_start - 1));
|
||||
while (it2 != m_globalTokenRange.end()) {
|
||||
|
||||
if (it->end < it2->start)
|
||||
ranges.insert(Interval(it->end, it2->start));
|
||||
if (it->m_end < it2->m_start)
|
||||
ranges.insert(TokenInterval(it->m_end + 1, it2->m_start - 1));
|
||||
else
|
||||
ranges.insert(Interval(it->start, it2->end));
|
||||
ranges.insert(TokenInterval(it->m_start, it2->m_end));
|
||||
it = it2;
|
||||
it2 = std::next(it);
|
||||
}
|
||||
|
||||
if (it->end < (i32) (tokenCount-1))
|
||||
ranges.insert(Interval(it->end, tokenCount-1));
|
||||
if (it->m_end < (i32) (tokenCount-1))
|
||||
ranges.insert(TokenInterval(it->m_end + 1, tokenCount-1));
|
||||
}
|
||||
m_globalTokenRange = ranges;
|
||||
}
|
||||
@@ -2315,8 +2290,11 @@ namespace hex::plugin::builtin {
|
||||
void TextHighlighter::getTokenIdForArgument(i32 start, i32 argNumber, Token delimiter) {
|
||||
m_curr = m_startToken;
|
||||
next(start);
|
||||
while (!peek(delimiter))
|
||||
while (!peek(delimiter)) {
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
next();
|
||||
}
|
||||
next();
|
||||
i32 count = 0;
|
||||
while (count < argNumber && !peek(tkn::Separator::EndOfProgram)) {
|
||||
@@ -2336,7 +2314,7 @@ namespace hex::plugin::builtin {
|
||||
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);
|
||||
auto argumentIndex = getArgumentNumber(tokenRange[name].m_start, definition.tokenIndex);
|
||||
|
||||
if (tokenRange == m_UDTTokenRange || !m_attributeFunctionArgumentType.contains(name) ||
|
||||
m_attributeFunctionArgumentType[name].empty()) {
|
||||
@@ -2413,35 +2391,40 @@ namespace hex::plugin::builtin {
|
||||
// Calculates the union of all the UDT and function token ranges
|
||||
// and inverts the result.
|
||||
void TextHighlighter::getGlobalTokenRanges() {
|
||||
std::set<Interval> ranges;
|
||||
std::set<TokenInterval> 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);
|
||||
if (!ranges.empty()) {
|
||||
auto rit = ranges.rbegin();
|
||||
auto rnext = std::next(rit);
|
||||
while (rnext != ranges.rend()) {
|
||||
if (rit->contains(*rnext)) {
|
||||
ranges.erase(*rnext);
|
||||
rnext = std::next(rit);
|
||||
} else if (rnext->contiguous(*rit)) {
|
||||
TokenInterval range = *rnext;
|
||||
range.m_end = rit->m_end;
|
||||
ranges.erase(*rnext);
|
||||
ranges.erase(*rit);
|
||||
ranges.insert(range);
|
||||
rit = std::set<TokenInterval>::reverse_iterator(ranges.find(range));
|
||||
rit--;
|
||||
rnext = std::next(rit);
|
||||
} else {
|
||||
rit++;
|
||||
rnext = std::next(rit);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_globalTokenRange = ranges;
|
||||
invertGlobalTokenRange();
|
||||
for (auto tokenRange: m_globalTokenRange) {
|
||||
|
||||
if ((u32) tokenRange.end == m_requiredInputs.fullTokens.size()) {
|
||||
tokenRange.end -= 1;
|
||||
if ((u32) tokenRange.m_end == m_requiredInputs.fullTokens.size()) {
|
||||
tokenRange.m_end -= 1;
|
||||
m_globalBlocks.insert(tokenRange);
|
||||
}
|
||||
}
|
||||
@@ -2452,8 +2435,8 @@ namespace hex::plugin::builtin {
|
||||
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());
|
||||
auto startToken = TokenIter(m_requiredInputs.fullTokens.begin() + range.m_start, m_requiredInputs.fullTokens.begin() + range.m_end);
|
||||
auto endToken = TokenIter(m_requiredInputs.fullTokens.begin() + range.m_end, m_requiredInputs.fullTokens.end());
|
||||
|
||||
for (m_curr = startToken; endToken > m_curr; next()) {
|
||||
|
||||
@@ -2526,7 +2509,7 @@ namespace hex::plugin::builtin {
|
||||
m_globalTokenRange.clear();
|
||||
|
||||
if (m_requiredInputs.fullTokens.size() > 1) {
|
||||
m_globalTokenRange.insert(Interval(0, m_requiredInputs.fullTokens.size() - 1));
|
||||
m_globalTokenRange.insert(TokenInterval(0, m_requiredInputs.fullTokens.size() - 1));
|
||||
getTokenRanges(IdentifierType::NameSpace);
|
||||
getTokenRanges(IdentifierType::UDT);
|
||||
getTokenRanges(IdentifierType::Function);
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
#include <pl/core/ast/ast_node_variable_decl.hpp>
|
||||
#include <pl/core/ast/ast_node_builtin_type.hpp>
|
||||
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/magic.hpp>
|
||||
#include <hex/helpers/binary_pattern.hpp>
|
||||
#include <hex/helpers/default_paths.hpp>
|
||||
#include <banners/banner_button.hpp>
|
||||
|
||||
@@ -1474,6 +1476,7 @@ namespace hex::plugin::builtin {
|
||||
TaskManager::createBackgroundTask("HighlightSourceCode", [this,provider](auto &) { m_textHighlighter.get(provider).highlightSourceCode(); });
|
||||
} else if (m_changesWereColored && !m_allStepsCompleted) {
|
||||
m_textHighlighter.get(provider).setRequestedIdentifierColors();
|
||||
m_textEditor.get(provider).getLines().readHiddenLines();
|
||||
m_allStepsCompleted = true;
|
||||
}
|
||||
|
||||
@@ -1639,6 +1642,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
this->evaluatePattern(code, provider);
|
||||
m_textEditor.get(provider).setText(code, true);
|
||||
m_textEditor.get(provider).readHiddenLines();
|
||||
m_sourceCode.get(provider) = code;
|
||||
if (trackFile) {
|
||||
m_changeTracker.get(provider) = wolv::io::ChangeTracker(file);
|
||||
@@ -1853,6 +1857,7 @@ namespace hex::plugin::builtin {
|
||||
m_consoleEditor.get(provider).setReadOnly(true);
|
||||
m_consoleEditor.get(provider).setShowCursor(false);
|
||||
m_consoleEditor.get(provider).setShowLineNumbers(false);
|
||||
m_consoleEditor.get(provider).getLines().enableCodeFolds(false);
|
||||
m_consoleEditor.get(provider).setSourceCodeEditor(&m_textEditor.get(provider));
|
||||
std::string sourcecode = pl::api::Source::DefaultSource;
|
||||
std::string error = "E: ";
|
||||
@@ -1869,7 +1874,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
EventProviderChanged::subscribe(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
|
||||
if (oldProvider != nullptr) {
|
||||
m_sourceCode.get(oldProvider) = m_textEditor.get(oldProvider).getText();
|
||||
m_sourceCode.get(oldProvider) = m_textEditor.get(oldProvider).getText(true);
|
||||
m_scroll.get(oldProvider) = m_textEditor.get(oldProvider).getScroll();
|
||||
m_cursorPosition.get(oldProvider) = m_textEditor.get(oldProvider).getCursorPosition();
|
||||
m_selection.get(oldProvider) = m_textEditor.get(oldProvider).getSelection();
|
||||
@@ -1882,7 +1887,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (newProvider != nullptr) {
|
||||
m_textEditor.get(newProvider).setText(wolv::util::preprocessText(m_sourceCode.get(newProvider)));
|
||||
m_textEditor.get(newProvider).setCursorPosition(m_cursorPosition.get(newProvider),false);
|
||||
m_textEditor.get(newProvider).setCursorPosition(m_cursorPosition.get(newProvider),false,false);
|
||||
m_textEditor.get(newProvider).setScroll(m_scroll.get(newProvider));
|
||||
m_textEditor.get(newProvider).setSelection(m_selection.get(newProvider));
|
||||
m_textEditor.get(newProvider).setBreakpoints(m_breakpoints.get(newProvider));
|
||||
@@ -2535,7 +2540,37 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + SHIFT + Keys::M + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.move_matched_bracket", [this] {
|
||||
if (auto editor = getEditorFromFocusedWindow(); editor != nullptr)
|
||||
editor->moveToMatchedBracket(false);
|
||||
editor->moveToMatchedDelimiter(false);
|
||||
});
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + Keys::KeyPadAdd + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.code_fold_expand", [this] {
|
||||
if (m_focusedSubWindowName.contains(TextEditorView))
|
||||
m_textEditor.get(ImHexApi::Provider::get()).codeFoldExpand();
|
||||
});
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + ALT + Keys::KeyPadAdd + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.code_fold_expand_recursively", [this] {
|
||||
if (m_focusedSubWindowName.contains(TextEditorView))
|
||||
m_textEditor.get(ImHexApi::Provider::get()).codeFoldExpand(0,true,false);
|
||||
});
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + SHIFT + Keys::KeyPadAdd + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.code_fold_expand_all", [this] {
|
||||
if (m_focusedSubWindowName.contains(TextEditorView))
|
||||
m_textEditor.get(ImHexApi::Provider::get()).codeFoldExpand(0, false, true);
|
||||
});
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + Keys::KeyPadSubtract + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.code_fold_collapse", [this] {
|
||||
if (m_focusedSubWindowName.contains(TextEditorView))
|
||||
m_textEditor.get(ImHexApi::Provider::get()).codeFoldCollapse();
|
||||
});
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + ALT + Keys::KeyPadSubtract + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.code_fold_collapse_recursively", [this] {
|
||||
if (m_focusedSubWindowName.contains(TextEditorView))
|
||||
m_textEditor.get(ImHexApi::Provider::get()).codeFoldCollapse(0, true, false);
|
||||
});
|
||||
|
||||
ShortcutManager::addShortcut(this, CTRLCMD + SHIFT + Keys::KeyPadSubtract + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.code_fold_collapse_all", [this] {
|
||||
if (m_focusedSubWindowName.contains(TextEditorView))
|
||||
m_textEditor.get(ImHexApi::Provider::get()).codeFoldCollapse(0, false, true);
|
||||
});
|
||||
|
||||
// Generate pattern code report
|
||||
@@ -2706,7 +2741,7 @@ namespace hex::plugin::builtin {
|
||||
fs::DialogMode::Save, { {"Pattern File", "hexpat"}, {"Pattern Import File", "pat"} },
|
||||
[this, provider, trackFile](const auto &path) {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeString(wolv::util::trim(m_textEditor.get(provider).getText()));
|
||||
file.writeString(wolv::util::trim(m_textEditor.get(provider).getText(true)));
|
||||
m_patternFileDirty.get(provider) = false;
|
||||
auto loadedPath = m_changeTracker.get(provider).getPath();
|
||||
if ((loadedPath.empty() && loadedPath != path) || (!loadedPath.empty() && !trackFile) || loadedPath == path)
|
||||
@@ -2729,7 +2764,7 @@ namespace hex::plugin::builtin {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
if (file.isValid() && trackFile) {
|
||||
if (isPatternDirty(provider)) {
|
||||
file.writeString(wolv::util::trim(m_textEditor.get(provider).getText()));
|
||||
file.writeString(wolv::util::trim(m_textEditor.get(provider).getText(true)));
|
||||
m_patternFileDirty.get(provider) = false;
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -21,6 +21,7 @@ add_imhex_plugin(
|
||||
source/ui/text_editor/render.cpp
|
||||
source/ui/text_editor/support.cpp
|
||||
source/ui/text_editor/utf8.cpp
|
||||
source/ui/text_editor/codeFolder.cpp
|
||||
INCLUDES
|
||||
include
|
||||
LIBRARIES
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
919
plugins/ui/source/ui/text_editor/codeFolder.cpp
Normal file
919
plugins/ui/source/ui/text_editor/codeFolder.cpp
Normal file
@@ -0,0 +1,919 @@
|
||||
#include <ui/text_editor.hpp>
|
||||
#include <pl/core/tokens.hpp>
|
||||
#include <pl/core/token.hpp>
|
||||
#include <wolv/utils/string.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <pl/api.hpp>
|
||||
|
||||
namespace hex::ui {
|
||||
using namespace pl::core;
|
||||
using Interval = TextEditor::Interval;
|
||||
using Token = pl::core::Token;
|
||||
using Coordinates = TextEditor::Coordinates;
|
||||
|
||||
/*
|
||||
std::pair<i32, i32> TextEditor::convertIndexToLineNumbers(Interval interval) {
|
||||
if (m_tokens[interval.m_start].location.source->mainSource && m_tokens[interval.m_end].location.source->mainSource)
|
||||
return std::make_pair(m_tokens[interval.m_start].location.line, m_tokens[interval.m_end].location.line);
|
||||
return std::make_pair(0, 0);
|
||||
}
|
||||
*/
|
||||
void TextEditor::Lines::skipAttribute() {
|
||||
|
||||
if (sequence(tkn::Separator::LeftBracket, tkn::Separator::LeftBracket)) {
|
||||
while (!sequence(tkn::Separator::RightBracket, tkn::Separator::RightBracket))
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Interval> TextEditor::Lines::searchRangeForBlocks(Interval interval) {
|
||||
m_curr = m_startToken + interval.m_start;
|
||||
std::vector<Interval> result;
|
||||
|
||||
u32 nestedLevel = 0;
|
||||
std::vector<i32> tokenStack;
|
||||
while (m_curr != m_startToken + interval.m_end) {
|
||||
|
||||
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 result;
|
||||
Interval range(tokenStack.back(), getTokenId(m_curr[-1].location));
|
||||
tokenStack.pop_back();
|
||||
|
||||
result.push_back(range);
|
||||
|
||||
if (nestedLevel == 0) {
|
||||
skipAttribute();
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (peek(tkn::Separator::EndOfProgram))
|
||||
return result;
|
||||
else
|
||||
next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Interval TextEditor::Lines::findBlockInRange(Interval interval) {
|
||||
Interval result = NotValid;
|
||||
auto tokenStart = TokenIter(m_tokens.begin(), m_tokens.end());
|
||||
|
||||
bool foundKey = false;
|
||||
bool foundComment = false;
|
||||
m_curr = tokenStart + interval.m_start;
|
||||
while (interval.m_end >= getTokenId(m_curr->location)) {
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return NotValid;
|
||||
if (result.m_start = getTokenId(m_curr->location); result.m_start < 0)
|
||||
return NotValid;
|
||||
|
||||
while (true) {
|
||||
if (const auto *docComment = const_cast<Token::DocComment *>(getValue<Token::DocComment>(0)); docComment != nullptr) {
|
||||
if (foundKey)
|
||||
break;
|
||||
if (docComment->singleLine) {
|
||||
foundComment = true;
|
||||
next();
|
||||
} else {
|
||||
if (foundComment)
|
||||
break;
|
||||
return {result.m_start, result.m_start};
|
||||
}
|
||||
} else if (const auto *comment = const_cast<Token::Comment *>(getValue<Token::Comment>(0)); comment != nullptr) {
|
||||
if (foundKey)
|
||||
break;
|
||||
if (comment->singleLine) {
|
||||
foundComment = true;
|
||||
next();
|
||||
} else {
|
||||
if (foundComment)
|
||||
break;
|
||||
return {result.m_start, result.m_start};
|
||||
}
|
||||
} else if (const auto *keyword = const_cast<Token::Keyword *>(getValue<Token::Keyword>(0));keyword != nullptr && *keyword == Token::Keyword::Import) {
|
||||
if (foundComment)
|
||||
break;
|
||||
foundKey = true;
|
||||
while (!peek(tkn::Separator::Semicolon) && !peek(tkn::Separator::EndOfProgram))
|
||||
next();
|
||||
next();
|
||||
} else if (const auto *directive = const_cast<Token::Directive *>(getValue<Token::Directive>(0));directive != nullptr && *directive == Token::Directive::Include) {
|
||||
if (foundComment)
|
||||
break;
|
||||
foundKey = true;
|
||||
u32 line = m_curr->location.line;
|
||||
while (m_curr->location.line == line && !peek(tkn::Separator::EndOfProgram))
|
||||
next();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (foundKey || foundComment) {
|
||||
auto currentId = getTokenId(m_curr->location);
|
||||
if (peek(tkn::Separator::EndOfProgram) || (currentId > 0 && currentId < (i32) m_tokens.size())) {
|
||||
next(-1);
|
||||
if (result.m_end = getTokenId(m_curr->location); result.m_end < 0)
|
||||
return NotValid;
|
||||
|
||||
return result;
|
||||
} else
|
||||
return NotValid;
|
||||
}
|
||||
next();
|
||||
}
|
||||
return NotValid;
|
||||
}
|
||||
|
||||
Coordinates TextEditor::Lines::findCommentEndCoord(i32 tokenId) {
|
||||
Coordinates result = Invalid;
|
||||
auto save = m_curr;
|
||||
m_curr = TokenIter(m_tokens.begin(), m_tokens.end()) + tokenId;
|
||||
if (peek(tkn::Literal::Comment)) {
|
||||
auto comment = getValue<Token::Comment>(0);
|
||||
if (comment != nullptr) {
|
||||
auto location = m_curr->location;
|
||||
if (comment->singleLine)
|
||||
return Coordinates(location.line - 1, location.column + location.length - 2);
|
||||
|
||||
std::string commentText = comment->comment;
|
||||
auto vectorString = wolv::util::splitString(commentText, "\n");
|
||||
size_t lineCount = vectorString.size();
|
||||
auto endColumn = (lineCount == 1) ? location.column + location.length - 1 : vectorString.back().length() + 1;
|
||||
return Coordinates(location.line + lineCount - 2, endColumn);
|
||||
}
|
||||
|
||||
} else if (peek(tkn::Literal::DocComment)) {
|
||||
auto docComment = getValue<Token::DocComment>(0);
|
||||
if (docComment != nullptr) {
|
||||
auto location = m_curr->location;
|
||||
if (docComment->singleLine)
|
||||
return Coordinates(location.line - 1, location.column + location.length - 2);
|
||||
|
||||
std::string commentText = docComment->comment;
|
||||
auto vectorString = wolv::util::splitString(commentText, "\n");
|
||||
size_t lineCount = vectorString.size();
|
||||
auto endColumn = (lineCount == 1) ? location.column + location.length - 1 : vectorString.back().length() + 1;
|
||||
return Coordinates(location.line + lineCount - 2, endColumn);
|
||||
}
|
||||
}
|
||||
m_curr = save;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::set<Interval> TextEditor::Lines::blocksFromGlobal() {
|
||||
std::set<Interval> result;
|
||||
if (m_globalBlocks.size() == 1)
|
||||
return m_globalBlocks;
|
||||
auto globalsIter = m_globalBlocks.begin();
|
||||
bool absorbPreviousToken = false;
|
||||
while (globalsIter != m_globalBlocks.end()) {
|
||||
if (absorbPreviousToken && globalsIter->m_start > 0) {
|
||||
result.insert(Interval(globalsIter->m_start - 1, globalsIter->m_end));
|
||||
absorbPreviousToken = false;
|
||||
} else if (globalsIter->m_start == globalsIter->m_end) {
|
||||
absorbPreviousToken = true;
|
||||
} else
|
||||
result.insert(*globalsIter);
|
||||
auto nextIter = std::next(globalsIter);
|
||||
|
||||
if (nextIter != m_globalBlocks.end() && absorbPreviousToken) {
|
||||
result.insert(Interval(globalsIter->m_end, nextIter->m_start - 1));
|
||||
absorbPreviousToken = false;
|
||||
} else if (nextIter != m_globalBlocks.end() && globalsIter->m_end + 1 < nextIter->m_start - 1)
|
||||
result.insert(Interval(globalsIter->m_end + 1, nextIter->m_start - 1));
|
||||
else if (nextIter != m_globalBlocks.end() && globalsIter->m_end + 1 == nextIter->m_start - 1)
|
||||
absorbPreviousToken = true;
|
||||
else if (globalsIter->m_end + 1 < (i32) m_tokens.size() - 1)
|
||||
result.insert(Interval(globalsIter->m_end + 1, (i32) m_tokens.size() - 1));
|
||||
|
||||
globalsIter++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//comments imports and includes
|
||||
void TextEditor::Lines::nonDelimitedFolds() {
|
||||
//auto allBlocks = blocksFromGlobal();
|
||||
auto size = m_tokens.size();
|
||||
if (size > 0) {
|
||||
Interval block = {0,static_cast<i32>(size-1)};
|
||||
//for (auto block: allBlocks) {
|
||||
while (true) {
|
||||
auto interval = findBlockInRange(block);
|
||||
|
||||
if (interval == NotValid)
|
||||
break;
|
||||
|
||||
Coordinates endCoord, startCoord = Coordinates(m_tokens[interval.m_start].location);
|
||||
|
||||
if (interval.m_end == interval.m_start)
|
||||
endCoord = findCommentEndCoord(interval.m_start);
|
||||
else
|
||||
endCoord = Coordinates(m_tokens[interval.m_end].location);
|
||||
|
||||
if (startCoord.getLine() != endCoord.getLine())
|
||||
m_foldPoints[startCoord] = endCoord;
|
||||
|
||||
if (interval.m_end >= block.m_end)
|
||||
break;
|
||||
block.m_start = interval.m_end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Coordinates, Coordinates> TextEditor::Lines::getDelimiterLineNumbers(i32 start, i32 end, const std::string &delimiters) {
|
||||
std::pair<Coordinates, Coordinates> result = {Invalid, Invalid};
|
||||
Coordinates first = Invalid;
|
||||
auto tokenStart = TokenIter(m_tokens.begin(), m_tokens.end());
|
||||
m_curr = tokenStart + start;
|
||||
Token::Separator openSeparator = Token::Separator::EndOfProgram;
|
||||
Token::Separator closeSeparator = Token::Separator::EndOfProgram;
|
||||
Token::Operator openOperator, closeOperator;
|
||||
if (delimiters == "{}") {
|
||||
openSeparator = Token::Separator::LeftBrace;
|
||||
closeSeparator = Token::Separator::RightBrace;
|
||||
} else if (delimiters == "[]") {
|
||||
openSeparator = Token::Separator::LeftBracket;
|
||||
closeSeparator = Token::Separator::RightBracket;
|
||||
} else if (delimiters == "()") {
|
||||
openSeparator = Token::Separator::LeftParenthesis;
|
||||
closeSeparator = Token::Separator::RightParenthesis;
|
||||
} else if (delimiters == "<>") {
|
||||
openOperator = Token::Operator::BoolLessThan;
|
||||
closeOperator = Token::Operator::BoolGreaterThan;
|
||||
}
|
||||
|
||||
Token::Separator *separator;
|
||||
if (separator = const_cast<Token::Separator *>(getValue<Token::Separator>(0)); separator == nullptr || *separator != openSeparator) {
|
||||
if (const auto *opener = const_cast<Token::Operator *>(getValue<Token::Operator>(0)); opener == nullptr || *opener != openOperator)
|
||||
return result;
|
||||
}
|
||||
if (!m_curr->location.source->mainSource)
|
||||
return result;
|
||||
if (start > 0) {
|
||||
Location location1 = m_curr->location;
|
||||
Location location2;
|
||||
auto save = m_curr;
|
||||
while (peek(tkn::Literal::Comment, -1) || peek(tkn::Literal::DocComment, -1)) {
|
||||
if (getTokenId(m_curr->location) == 0)
|
||||
break;
|
||||
next(-1);
|
||||
}
|
||||
next(-1);
|
||||
if (separator != nullptr && *separator == Token::Separator::LeftParenthesis) {
|
||||
if (const auto *separator2 = const_cast<Token::Separator *>(getValue<Token::Separator>(0)); separator2 != nullptr && (*separator2 == Token::Separator::Semicolon || *separator2 == Token::Separator::LeftBrace || *separator2 == Token::Separator::RightBrace)) {
|
||||
m_curr = save;
|
||||
location2 = m_curr->location;
|
||||
} else {
|
||||
location2 = m_curr->location;
|
||||
m_curr = save;
|
||||
}
|
||||
} else {
|
||||
location2 = m_curr->location;
|
||||
m_curr = save;
|
||||
}
|
||||
if (location1.line != location2.line) {
|
||||
Coordinates coord(location2);
|
||||
first = coord + Coordinates(0, location2.length);
|
||||
} else
|
||||
first = Coordinates(location1);
|
||||
} else
|
||||
first = Coordinates(m_curr->location);
|
||||
m_curr = tokenStart + end;
|
||||
if (separator = const_cast<Token::Separator *>(getValue<Token::Separator>(0)); separator == nullptr || *separator != closeSeparator) {
|
||||
if (const auto *closer = const_cast<Token::Operator *>(getValue<Token::Operator>(0)); closer == nullptr || *closer != closeOperator) {
|
||||
if (const auto *separator2 = const_cast<Token::Separator *>(getValue<Token::Separator>(1)); separator2 != nullptr && *separator2 == closeSeparator) {
|
||||
next();
|
||||
} else if (const auto *closer2 = const_cast<Token::Operator *>(getValue<Token::Operator>(1)); closer2 != nullptr && *closer2 == closeOperator) {
|
||||
next();
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (!m_curr->location.source->mainSource)
|
||||
return result;
|
||||
|
||||
result.first = first;
|
||||
result.second = Coordinates(m_curr->location);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::advanceToNextLine(i32 &lineIndex, i32 ¤tTokenId, Location &location) {
|
||||
if (lineIndex = nextLine(lineIndex); lineIndex >= size())
|
||||
return;
|
||||
currentTokenId = m_firstTokenIdOfLine[lineIndex];
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
location = m_curr->location;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::advanceTokenId(i32 &lineIndex, i32 ¤tTokenId, Location &location) {
|
||||
currentTokenId++;
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::moveToLocationColumn(i32 locationColumn, i32 ¤tTokenId, Location &location) {
|
||||
location.column = locationColumn;
|
||||
location.length = 1;
|
||||
if (currentTokenId = getTokenId(location); currentTokenId < 0)
|
||||
return;
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::resetToTokenId(i32 &lineIndex, i32 ¤tTokenId, Location &location) {
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
}
|
||||
|
||||
|
||||
TextEditor::CodeFoldBlocks TextEditor::Lines::foldPointsFromSource() {
|
||||
loadFirstTokenIdOfLine();
|
||||
if (m_firstTokenIdOfLine.empty())
|
||||
return m_foldPoints;
|
||||
m_foldPoints.clear();
|
||||
nonDelimitedFolds();
|
||||
std::string blockDelimiters = "{[(<";
|
||||
size_t topLine = 0;
|
||||
i32 bottomLine = size();
|
||||
m_startToken = TokenIter(m_tokens.begin(), m_tokens.end());
|
||||
m_curr = m_startToken;
|
||||
auto location = m_curr->location;
|
||||
i32 lineIndex = topLine;
|
||||
i32 currentTokenId = 0;
|
||||
while (lineIndex < bottomLine) {
|
||||
auto line = operator[](lineIndex);
|
||||
if (line.empty()) {
|
||||
advanceToNextLine(lineIndex, currentTokenId, location);
|
||||
if (lineIndex >= bottomLine) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (size_t columnIndex = line.m_chars.find_first_of(blockDelimiters, location.column - 1); columnIndex != std::string::npos) {
|
||||
std::string openDelimiter = std::string(1, line[columnIndex]);
|
||||
moveToLocationColumn(columnIndex + 1, currentTokenId, location);
|
||||
if (currentTokenId < 0) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
|
||||
if (m_curr[0].getFormattedType() != "Operator" && m_curr[0].getFormattedType() != "Separator") {
|
||||
if (currentTokenId >= (i32) m_tokens.size()) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
advanceTokenId(lineIndex, currentTokenId, location);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto idx = blockDelimiters.find(openDelimiter); idx != std::string::npos) {
|
||||
if (idx == 3) {
|
||||
if (currentTokenId == 0) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
next(-1);
|
||||
if (const auto *identifier = const_cast<Token::Identifier *>(getValue<Token::Identifier>(0)); identifier == nullptr || identifier->getType() != Token::Identifier::IdentifierType::UDT) {
|
||||
next(2);
|
||||
if (peek(tkn::Separator::EndOfProgram, -1)) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
|
||||
if (currentTokenId = getTokenId(m_curr->location); currentTokenId < 0) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
resetToTokenId(lineIndex, currentTokenId, location);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto start = currentTokenId;
|
||||
auto end = findMatchingDelimiter(currentTokenId);
|
||||
if (end.first < 0) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
std::string value = openDelimiter + end.second;
|
||||
auto lineBased = getDelimiterLineNumbers(start, end.first, value);
|
||||
|
||||
if (lineBased.first.getLine() != lineBased.second.getLine())
|
||||
m_foldPoints[lineBased.first] = lineBased.second;
|
||||
|
||||
if (currentTokenId = getTokenId(m_tokens[end.first].location); currentTokenId < 0 || currentTokenId >= (i32) m_tokens.size()) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
advanceTokenId(lineIndex, currentTokenId, location);
|
||||
} else {
|
||||
return m_foldPoints;
|
||||
}
|
||||
} else {
|
||||
advanceToNextLine(lineIndex, currentTokenId, location);
|
||||
if (lineIndex >= bottomLine) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_foldPoints;
|
||||
}
|
||||
|
||||
|
||||
std::pair<i32, char> TextEditor::Lines::findMatchingDelimiter(i32 from) {
|
||||
std::string blockDelimiters = "{}[]()<>";
|
||||
std::pair<i32, char> result = std::make_pair(-1, '\0');
|
||||
auto tokenStart = TokenIter(m_tokens.begin(), m_tokens.end());
|
||||
if (from >= (i32) m_tokens.size())
|
||||
return result;
|
||||
m_curr = tokenStart + from;
|
||||
Location location = m_curr->location;
|
||||
auto line = operator[](location.line - 1);
|
||||
std::string openDelimiter;
|
||||
if (auto columnIndex = line.m_chars.find_first_of(blockDelimiters, location.column - 1); columnIndex != std::string::npos)
|
||||
openDelimiter = line[columnIndex];
|
||||
else
|
||||
return result;
|
||||
|
||||
i32 currentTokenId = from + 1;
|
||||
std::string closeDelimiter;
|
||||
if (size_t idx = blockDelimiters.find(openDelimiter); idx != std::string::npos && currentTokenId < (i32) m_tokens.size())
|
||||
closeDelimiter = blockDelimiters[idx + 1];
|
||||
else
|
||||
return result;
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
location = m_curr->location;
|
||||
size_t lineIndex = location.line - 1;
|
||||
size_t bottomLine = size();
|
||||
while (lineIndex < bottomLine) {
|
||||
line = operator[](lineIndex);
|
||||
if (line.empty()) {
|
||||
|
||||
if (lineIndex = nextLine(lineIndex); lineIndex >= bottomLine)
|
||||
return result;
|
||||
currentTokenId = m_firstTokenIdOfLine[lineIndex];
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
location = m_curr->location;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto columnIndex = line.m_chars.find_first_of(blockDelimiters, location.column - 1); columnIndex != std::string::npos) {
|
||||
std::string currentChar = std::string(1, line[columnIndex]);
|
||||
location.column = columnIndex + 1;
|
||||
location.length = 1;
|
||||
|
||||
if (currentTokenId = getTokenId(location); currentTokenId < 0)
|
||||
return result;
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
if (m_curr[0].getFormattedType() != "Operator" && m_curr[0].getFormattedType() != "Separator") {
|
||||
|
||||
if (currentTokenId >= (i32) m_tokens.size())
|
||||
return result;
|
||||
currentTokenId++;
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto idx = blockDelimiters.find(currentChar); idx != std::string::npos) {
|
||||
if (currentChar == closeDelimiter) {
|
||||
|
||||
if (currentTokenId = getTokenId(location); currentTokenId < 0)
|
||||
return result;
|
||||
return std::make_pair(currentTokenId, closeDelimiter[0]);
|
||||
} else {
|
||||
if (idx == 6 || idx == 7) {
|
||||
next(-1);
|
||||
if (const auto *identifier = const_cast<Token::Identifier *>(getValue<Token::Identifier>(0));
|
||||
((idx == 6) && (identifier == nullptr || identifier->getType() != Token::Identifier::IdentifierType::UDT)) || (idx == 7)) {
|
||||
next(2);
|
||||
if (peek(tkn::Separator::EndOfProgram, -1)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (currentTokenId = getTokenId(m_curr->location); currentTokenId < 0)
|
||||
return result;
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (idx % 2 == 0) {
|
||||
auto start = currentTokenId;
|
||||
auto end = findMatchingDelimiter(currentTokenId);
|
||||
if (end.first < 0)
|
||||
return result;
|
||||
std::string value = currentChar + end.second;
|
||||
auto lineBased = getDelimiterLineNumbers(start, end.first, value);
|
||||
if (lineBased.first.getLine() != lineBased.second.getLine())
|
||||
m_foldPoints[lineBased.first] = lineBased.second;
|
||||
|
||||
if (currentTokenId = getTokenId(m_tokens[end.first].location); currentTokenId < 0 || currentTokenId >= (i32) m_tokens.size())
|
||||
return result;
|
||||
|
||||
currentTokenId++;
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (lineIndex = nextLine(lineIndex); lineIndex >= bottomLine)
|
||||
return result;
|
||||
currentTokenId = m_firstTokenIdOfLine[lineIndex];
|
||||
m_curr = tokenStart + currentTokenId;
|
||||
location = m_curr->location;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void TextEditor::saveCodeFoldStates() {
|
||||
m_lines.saveCodeFoldStates();
|
||||
}
|
||||
|
||||
void TextEditor::Lines::saveCodeFoldStates() {
|
||||
i32 codeFoldIndex = 0;
|
||||
std::vector<i32> closedFoldIncrements;
|
||||
for (auto key: m_codeFoldKeys) {
|
||||
if (m_codeFoldState.contains(key) && !m_codeFoldState[key]) {
|
||||
closedFoldIncrements.push_back(codeFoldIndex);
|
||||
codeFoldIndex = 1;
|
||||
} else
|
||||
codeFoldIndex++;
|
||||
}
|
||||
std::string result = "//+-#:";
|
||||
for (u32 i = 0; i < closedFoldIncrements.size(); ++i) {
|
||||
result += std::to_string(closedFoldIncrements[i]);
|
||||
if (i < closedFoldIncrements.size() - 1)
|
||||
result += ",";
|
||||
}
|
||||
auto lineIndex = 0;
|
||||
HiddenLine hiddenLine(lineIndex, result);
|
||||
if (!m_hiddenLines.empty()) {
|
||||
m_hiddenLines[0] = hiddenLine;
|
||||
return;
|
||||
}
|
||||
m_hiddenLines.push_back(hiddenLine);
|
||||
}
|
||||
|
||||
void TextEditor::applyCodeFoldStates() {
|
||||
m_lines.applyCodeFoldStates();
|
||||
}
|
||||
|
||||
void TextEditor::Lines::applyCodeFoldStates() {
|
||||
|
||||
std::string commentLine;
|
||||
for (auto line: m_hiddenLines) {
|
||||
if (line.m_line.starts_with("//+-#:")) {
|
||||
commentLine = line.m_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (commentLine.size() < 6 || !commentLine.starts_with("//+-#:"))
|
||||
return;
|
||||
auto states = commentLine.substr(6);
|
||||
auto stringVector = wolv::util::splitString(states, ",", true);
|
||||
auto count = stringVector.size();
|
||||
if (count == 1 && stringVector[0].empty())
|
||||
return;
|
||||
std::vector<i32> closedFoldIncrements(count);
|
||||
i32 value = 0;
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
auto stateStr = stringVector[i];
|
||||
std::from_chars(stateStr.data(), stateStr.data() + stateStr.size(), value);
|
||||
closedFoldIncrements[i] = value;
|
||||
}
|
||||
m_codeFoldState.clear();
|
||||
auto codeFoldKeyIter = m_codeFoldKeys.begin();
|
||||
i32 closeFold = 0;
|
||||
for (auto closedFoldIncrement: closedFoldIncrements) {
|
||||
closeFold += closedFoldIncrement;
|
||||
if (closeFold < 0 || closeFold >= (i32) m_codeFoldKeys.size())
|
||||
continue;
|
||||
std::advance(codeFoldKeyIter, closedFoldIncrement);
|
||||
m_codeFoldState[*codeFoldKeyIter] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::Lines::closeCodeFold(const Range &key, bool userTriggered) {
|
||||
float topRow;
|
||||
|
||||
if (userTriggered)
|
||||
topRow = m_topRow;
|
||||
LineIndexToScreen lineIndexToScreen;
|
||||
bool needsDelimiter = lineNeedsDelimiter(key.m_start.m_line);
|
||||
auto lineIter = m_lineIndexToScreen.begin();
|
||||
|
||||
while (lineIter != m_lineIndexToScreen.end() && lineIter->first <= key.m_start.m_line) {
|
||||
lineIndexToScreen[lineIter->first] = lineIter->second;
|
||||
lineIter++;
|
||||
}
|
||||
auto startingXScreenCoordinate = foldedCoordsToScreen(lineCoordinates(0, 0)).x;
|
||||
float currentYScreenCoordinate = 0;
|
||||
|
||||
if (needsDelimiter) {
|
||||
auto screenCoordinates = lineIter->second;
|
||||
auto &line = m_unfoldedLines[key.m_start.m_line];
|
||||
screenCoordinates.y = m_lineIndexToScreen[key.m_start.m_line].y;
|
||||
TrimMode trimMode = TrimMode::TrimBoth;
|
||||
auto row = lineIndexToRow(key.m_start.m_line);
|
||||
|
||||
if (m_foldedLines.contains(row) && m_foldedLines[row].m_full.m_start.m_line == key.m_start.m_line)
|
||||
trimMode = TrimMode::TrimEnd;
|
||||
screenCoordinates.x = m_lineIndexToScreen[key.m_start.m_line].x + line.trim(trimMode).lineTextSize();
|
||||
lineIndexToScreen[lineIter->first] = screenCoordinates;
|
||||
lineIter++;
|
||||
}
|
||||
while (lineIter != m_lineIndexToScreen.end()) {
|
||||
|
||||
if (lineIter->first >= key.m_end.m_line) {
|
||||
auto screenCoordinates = lineIter->second;
|
||||
if (lineIter->first == key.m_end.m_line && m_codeFoldDelimiters.contains(key) && !s_delimiters.contains(m_codeFoldDelimiters[key].first)) {
|
||||
lineIndexToScreen[lineIter->first] = ImVec2(-1, -1);
|
||||
lineIter++;
|
||||
continue;
|
||||
} else if (lineIter->first == key.m_end.m_line) {
|
||||
auto &line = m_unfoldedLines[key.m_start.m_line];
|
||||
screenCoordinates.y = m_lineIndexToScreen[key.m_start.m_line].y;
|
||||
currentYScreenCoordinate = screenCoordinates.y;
|
||||
TrimMode trim = TrimMode::TrimBoth;
|
||||
auto row = lineIndexToRow(key.m_start.m_line);
|
||||
|
||||
if (m_foldedLines.contains(row) && m_foldedLines[row].m_full.m_start.m_line == key.m_start.m_line)
|
||||
trim = TrimMode::TrimEnd;
|
||||
|
||||
screenCoordinates.x = m_lineIndexToScreen[key.m_start.m_line].x + line.trim(trim).lineTextSize() + Ellipsis.lineTextSize();
|
||||
|
||||
if (needsDelimiter) {
|
||||
Line bracketLine("{");
|
||||
screenCoordinates.x += bracketLine.lineTextSize();
|
||||
}
|
||||
} else if (screenCoordinates != ImVec2(-1, -1)) {
|
||||
screenCoordinates.y = currentYScreenCoordinate;
|
||||
if (screenCoordinates.x == startingXScreenCoordinate)
|
||||
screenCoordinates.y += m_charAdvance.y;
|
||||
currentYScreenCoordinate = screenCoordinates.y;
|
||||
}
|
||||
lineIndexToScreen[lineIter->first] = screenCoordinates;
|
||||
} else {
|
||||
lineIndexToScreen[lineIter->first] = ImVec2(-1, -1);
|
||||
}
|
||||
lineIter++;
|
||||
}
|
||||
m_lineIndexToScreen = std::move(lineIndexToScreen);
|
||||
|
||||
if (m_codeFoldState.contains(key) && m_codeFoldState[key])
|
||||
m_codeFoldState[key] = false;
|
||||
bool found = false;
|
||||
FoldedLine currentFoldedLine;
|
||||
for (auto [row, foldedLine]: m_foldedLines) {
|
||||
|
||||
if (!foldedLine.m_keys.empty() && (key.m_start.m_line == foldedLine.m_keys.back().m_end.m_line || key.m_end.m_line == foldedLine.m_keys.front().m_start.m_line)) {
|
||||
foldedLine.insertKey(key);
|
||||
foldedLine.m_row = row;
|
||||
m_foldedLines.erase(row);
|
||||
m_foldedLines[row] = foldedLine;
|
||||
found = true;
|
||||
currentFoldedLine = m_foldedLines[row];
|
||||
break;
|
||||
} else if (key.contains(foldedLine.m_full)) {
|
||||
FoldedLine newFoldedLine(this);
|
||||
newFoldedLine.insertKey(key);
|
||||
m_foldedLines.erase(row);
|
||||
m_foldedLines[newFoldedLine.m_row] = newFoldedLine;
|
||||
found = true;
|
||||
currentFoldedLine = m_foldedLines[newFoldedLine.m_row];
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
FoldedLine newFoldedLine(this);
|
||||
newFoldedLine.insertKey(key);
|
||||
|
||||
if (m_foldedLines.contains(newFoldedLine.m_row)) {
|
||||
const auto &foldedLine = m_foldedLines[newFoldedLine.m_row];
|
||||
|
||||
if (foldedLine.m_built) {
|
||||
newFoldedLine.m_foldedLine = foldedLine.m_foldedLine;
|
||||
newFoldedLine.m_ellipsisIndices = foldedLine.m_ellipsisIndices;
|
||||
newFoldedLine.m_cursorPosition = foldedLine.m_cursorPosition;
|
||||
newFoldedLine.m_built = true;
|
||||
}
|
||||
}
|
||||
m_foldedLines[newFoldedLine.m_row] = newFoldedLine;
|
||||
currentFoldedLine = m_foldedLines[newFoldedLine.m_row];
|
||||
}
|
||||
std::map<i32, FoldedLine> updatedFoldedLines;
|
||||
for (auto &[row, foldedLine] : m_foldedLines) {
|
||||
if (row > currentFoldedLine.m_row) {
|
||||
foldedLine.m_row -= (key.m_end.m_line - key.m_start.m_line);
|
||||
updatedFoldedLines[foldedLine.m_row] = foldedLine;
|
||||
} else {
|
||||
updatedFoldedLines[row] = foldedLine;
|
||||
}
|
||||
}
|
||||
m_foldedLines = std::move(updatedFoldedLines);
|
||||
if (userTriggered) {
|
||||
auto topLine = lineCoordinates( rowToLineIndex(topRow), 0);
|
||||
if (key.contains(topLine))
|
||||
m_topRow = lineIndexToRow(key.m_start.m_line);
|
||||
else
|
||||
m_topRow = topRow;
|
||||
m_setTopRow = true;
|
||||
m_saveCodeFoldStateRequested = true;
|
||||
}
|
||||
m_foldedLines[currentFoldedLine.m_row].loadSegments();
|
||||
}
|
||||
|
||||
void TextEditor::Lines::openCodeFold(const Range &key) {
|
||||
for (auto foldedLine : m_foldedLines) {
|
||||
for (auto foldKey : foldedLine.second.m_keys) {
|
||||
if (foldKey.contains(key) && foldKey != key) {
|
||||
m_codeFoldState[key] = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LineIndexToScreen indicesToScreen;
|
||||
auto lineIter = m_lineIndexToScreen.begin();
|
||||
while (lineIter != m_lineIndexToScreen.end() && lineIter->first < key.m_start.m_line) {
|
||||
indicesToScreen[lineIter->first] = lineIter->second;
|
||||
lineIter++;
|
||||
}
|
||||
|
||||
while (lineIter != m_lineIndexToScreen.end()) {
|
||||
if (lineIter->first + 1 > key.m_end.m_line) {
|
||||
auto sc = lineIter->second;
|
||||
if (sc != ImVec2(-1, -1))
|
||||
sc.y += m_charAdvance.y * (key.m_end.m_line - key.m_start.m_line);
|
||||
indicesToScreen[lineIter->first] = sc;
|
||||
} else {
|
||||
auto sc = ImVec2(m_cursorScreenPosition.x + m_leftMargin, m_lineIndexToScreen[key.m_start.m_line-1].y + m_charAdvance.y*(lineIter->first + 1 - key.m_start.m_line));
|
||||
indicesToScreen[lineIter->first] = sc;
|
||||
}
|
||||
lineIter++;
|
||||
}
|
||||
m_lineIndexToScreen = std::move(indicesToScreen);
|
||||
if (m_codeFoldState.contains(key) && !m_codeFoldState[key])
|
||||
m_codeFoldState[key]=true;
|
||||
i32 erasedRow = -1;
|
||||
for (auto &[row, foldedLine] : m_foldedLines) {
|
||||
if (std::any_of(foldedLine.m_keys.begin(), foldedLine.m_keys.end(), [key](const Range& currentKey) { return currentKey == key; })) {
|
||||
foldedLine.removeKey(key);
|
||||
if (foldedLine.m_keys.empty()) {
|
||||
m_foldedLines.erase(row);
|
||||
erasedRow = row;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (erasedRow != -1) {
|
||||
std::map<i32, FoldedLine> updatedFoldedLines;
|
||||
for (auto &[row, foldedLine] : m_foldedLines) {
|
||||
if (row > erasedRow) {
|
||||
foldedLine.m_row += (key.m_end.m_line - key.m_start.m_line);
|
||||
updatedFoldedLines[foldedLine.m_row] = foldedLine;
|
||||
} else {
|
||||
updatedFoldedLines[row] = foldedLine;
|
||||
}
|
||||
}
|
||||
m_foldedLines = std::move(updatedFoldedLines);
|
||||
}
|
||||
m_saveCodeFoldStateRequested = true;
|
||||
}
|
||||
|
||||
void TextEditor::openCodeFoldAt(Coordinates line) {
|
||||
for (auto fold : m_lines.m_codeFoldKeys) {
|
||||
if (fold.contains(line) && m_lines.m_codeFoldState.contains(fold) && !m_lines.m_codeFoldState[fold]) {
|
||||
m_lines.openCodeFold(fold);
|
||||
if (m_lines.m_lineIndexToScreen[line.m_line] != ImVec2(-1.0f, -1.0f))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *TextEditor::Lines::getValue(const i32 index) {
|
||||
return const_cast<T*>(std::get_if<T>(&m_curr[index].value));
|
||||
}
|
||||
|
||||
void TextEditor::Lines::next(i32 count) {
|
||||
if (m_interrupt) {
|
||||
m_interrupt = false;
|
||||
throw std::out_of_range("Highlights were deliberately interrupted");
|
||||
}
|
||||
if (count == 0)
|
||||
return;
|
||||
i32 id = getTokenId(m_curr->location);
|
||||
|
||||
if (count > 0)
|
||||
m_curr += std::min(count,static_cast<i32>(m_tokens.size() - id));
|
||||
else
|
||||
m_curr += -std::min(-count,id);
|
||||
|
||||
}
|
||||
constexpr static u32 Normal = 0;
|
||||
constexpr static u32 Not = 1;
|
||||
|
||||
bool TextEditor::Lines::begin() {
|
||||
m_originalPosition = m_curr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::partBegin() {
|
||||
m_partOriginalPosition = m_curr;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::reset() {
|
||||
m_curr = m_originalPosition;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::partReset() {
|
||||
m_curr = m_partOriginalPosition;
|
||||
}
|
||||
|
||||
bool TextEditor::Lines::resetIfFailed(const bool value) {
|
||||
if (!value) reset();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<auto S>
|
||||
bool TextEditor::Lines::sequenceImpl() {
|
||||
if constexpr (S == Normal)
|
||||
return true;
|
||||
else if constexpr (S == Not)
|
||||
return false;
|
||||
else
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
template<auto S>
|
||||
bool TextEditor::Lines::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 TextEditor::Lines::sequenceImpl(const auto &... args) {
|
||||
return (matchOne<S>(args) && ...);
|
||||
}
|
||||
|
||||
|
||||
template<auto S>
|
||||
bool TextEditor::Lines::sequence(const Token &token, const auto &... args) {
|
||||
partBegin();
|
||||
return sequenceImpl<S>(token, args...);
|
||||
}
|
||||
|
||||
|
||||
bool TextEditor::Lines::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 TextEditor::Lines::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_tokens.size())
|
||||
return false;
|
||||
return m_curr[index].type == token.type && m_curr[index] == token.value;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
||||
namespace hex::ui {
|
||||
extern TextEditor::Palette s_paletteBase;
|
||||
|
||||
|
||||
template<class InputIt1, class InputIt2, class BinaryPredicate>
|
||||
bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) {
|
||||
for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
|
||||
@@ -16,13 +17,13 @@ namespace hex::ui {
|
||||
}
|
||||
|
||||
void TextEditor::setNeedsUpdate(i32 line, bool needsUpdate) {
|
||||
if (line < (i32) m_lines.size())
|
||||
m_lines[line].setNeedsUpdate(needsUpdate);
|
||||
if (line < m_lines.size())
|
||||
m_lines.m_unfoldedLines[line].setNeedsUpdate(needsUpdate);
|
||||
}
|
||||
|
||||
void TextEditor::setColorizedLine(i64 line, const std::string &tokens) {
|
||||
if (line < (i64)m_lines.size()) {
|
||||
auto &lineTokens = m_lines[line].m_colors;
|
||||
if (line < m_lines.size()) {
|
||||
auto &lineTokens = m_lines.m_unfoldedLines[line].m_colors;
|
||||
if (lineTokens.size() != tokens.size()) {
|
||||
lineTokens.resize(tokens.size());
|
||||
std::ranges::fill(lineTokens, 0x00);
|
||||
@@ -40,11 +41,46 @@ namespace hex::ui {
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::colorize() {
|
||||
std::vector<TextEditor::Range> TextEditor::Lines::getDeactivatedBlocks() {
|
||||
colorizeInternal();
|
||||
std::vector<Range> deactivatedBlocks;
|
||||
if (isEmpty())
|
||||
return deactivatedBlocks;
|
||||
for (i32 i = 0; i < size(); ++i) {
|
||||
std::string flags = m_unfoldedLines[i].m_flags;
|
||||
if (flags.empty())
|
||||
continue;
|
||||
auto index = flags.find_first_not_of((char)8);
|
||||
Range currentBlock;
|
||||
bool foundStart = false;
|
||||
while (index == std::string::npos) {
|
||||
if (!foundStart) {
|
||||
currentBlock.m_start.m_line = i;
|
||||
currentBlock.m_start.m_column = 0;
|
||||
foundStart = true;
|
||||
} else {
|
||||
currentBlock.m_end.m_line = i;
|
||||
currentBlock.m_end.m_column = flags.size() - 1;
|
||||
}
|
||||
i++;
|
||||
if (i >= size())
|
||||
break;
|
||||
flags = m_unfoldedLines[i].m_flags;
|
||||
index = flags.find_first_not_of((char) 8);
|
||||
}
|
||||
if (currentBlock.m_start.m_line < currentBlock.m_end.m_line) {
|
||||
deactivatedBlocks.push_back(currentBlock);
|
||||
}
|
||||
}
|
||||
return deactivatedBlocks;
|
||||
|
||||
}
|
||||
|
||||
void TextEditor::Lines::colorize() {
|
||||
m_updateFlags = true;
|
||||
}
|
||||
|
||||
void TextEditor::colorizeRange() {
|
||||
void TextEditor::Lines::colorizeRange() {
|
||||
|
||||
if (isEmpty())
|
||||
return;
|
||||
@@ -56,9 +92,9 @@ namespace hex::ui {
|
||||
log::warn("Syntax highlighting tokenize callback is nullptr");
|
||||
return;
|
||||
}
|
||||
i32 linesSize = m_lines.size();
|
||||
i32 linesSize = size();
|
||||
for (i32 i = 0; i < linesSize; ++i) {
|
||||
auto &line = m_lines[i];
|
||||
auto &line = m_unfoldedLines[i];
|
||||
auto size = line.size();
|
||||
|
||||
if (line.m_colors.size() != size) {
|
||||
@@ -72,6 +108,7 @@ namespace hex::ui {
|
||||
auto last = line.end();
|
||||
|
||||
auto first = line.begin();
|
||||
line.m_colorized = true;
|
||||
for (auto current = first; (current - first) < (i64)size;) {
|
||||
strConstIter token_begin;
|
||||
strConstIter token_end;
|
||||
@@ -164,12 +201,12 @@ namespace hex::ui {
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::colorizeInternal() {
|
||||
void TextEditor::Lines::colorizeInternal() {
|
||||
if (isEmpty() || !m_colorizerEnabled)
|
||||
return;
|
||||
|
||||
if (m_updateFlags) {
|
||||
auto endLine = m_lines.size();
|
||||
auto endLine = size();
|
||||
auto commentStartLine = endLine;
|
||||
auto commentStartIndex = 0;
|
||||
auto withinGlobalDocComment = false;
|
||||
@@ -185,7 +222,7 @@ namespace hex::ui {
|
||||
ifDefs.push_back(true);
|
||||
m_defines.emplace_back("__IMHEX__");
|
||||
for (currentLine = 0; currentLine < endLine; currentLine++) {
|
||||
auto &line = m_lines[currentLine];
|
||||
auto &line = m_unfoldedLines[currentLine];
|
||||
auto lineLength = line.size();
|
||||
|
||||
if (line.m_flags.size() != lineLength) {
|
||||
@@ -213,9 +250,9 @@ namespace hex::ui {
|
||||
flags.m_value = (i32) Line::Comments::BlockDoc;
|
||||
flags.m_bits.deactivated = withinNotDef;
|
||||
flags.m_bits.matchedDelimiter = matchedBracket;
|
||||
if (m_lines[currentLine].m_flags[index] != flags.m_value) {
|
||||
m_lines[currentLine].m_colorized = false;
|
||||
m_lines[currentLine].m_flags[index] = flags.m_value;
|
||||
if (m_unfoldedLines[currentLine].m_flags[index] != flags.m_value) {
|
||||
m_unfoldedLines[currentLine].m_colorized = false;
|
||||
m_unfoldedLines[currentLine].m_flags[index] = flags.m_value;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -227,34 +264,34 @@ namespace hex::ui {
|
||||
char c = line[currentIndex];
|
||||
|
||||
matchedBracket = false;
|
||||
if (MatchedBracket::s_separators.contains(c) && m_matchedBracket.isActive()) {
|
||||
if (m_matchedBracket.m_nearCursor == getCharacterCoordinates(currentLine, currentIndex) || m_matchedBracket.m_matched == getCharacterCoordinates(currentLine, currentIndex))
|
||||
if (s_separators.contains(c) && m_matchedDelimiter.isActive()) {
|
||||
if (m_matchedDelimiter.m_nearCursor == lineIndexCoords(currentLine + 1, currentIndex) || m_matchedDelimiter.m_matched == lineIndexCoords(currentLine + 1, currentIndex))
|
||||
matchedBracket = true;
|
||||
} else if (MatchedBracket::s_operators.contains(c) && m_matchedBracket.isActive()) {
|
||||
Coordinates current = setCoordinates(currentLine,currentIndex);
|
||||
} else if (s_operators.contains(c) && m_matchedDelimiter.isActive()) {
|
||||
Coordinates current = lineCoordinates(currentLine, currentIndex);
|
||||
auto udt = static_cast<char>(PaletteIndex::UserDefinedType);
|
||||
Coordinates cursor = Invalid;
|
||||
//if (m_matchedBracket.m_nearCursor == setCoordinates(currentLine, currentIndex) || m_matchedBracket.m_matched == setCoordinates(currentLine, currentIndex)) {
|
||||
if ((c == '<' && m_matchedBracket.m_nearCursor == current) || (c == '>' && m_matchedBracket.m_matched == current))
|
||||
cursor = m_matchedBracket.m_nearCursor;
|
||||
else if ((c == '>' && m_matchedBracket.m_nearCursor == current) || (c == '<' && m_matchedBracket.m_matched == current))
|
||||
cursor = m_matchedBracket.m_matched;
|
||||
|
||||
if ((c == '<' && m_matchedDelimiter.m_nearCursor == current) || (c == '>' && m_matchedDelimiter.m_matched == current))
|
||||
cursor = m_matchedDelimiter.m_nearCursor;
|
||||
else if ((c == '>' && m_matchedDelimiter.m_nearCursor == current) || (c == '<' && m_matchedDelimiter.m_matched == current))
|
||||
cursor = m_matchedDelimiter.m_matched;
|
||||
|
||||
if (cursor != Invalid) {
|
||||
if (cursor.m_column == 0 && cursor.m_line > 0) {
|
||||
cursor.m_line--;
|
||||
cursor.m_column = m_lines[cursor.m_line].m_colors.size() - 1;
|
||||
cursor.m_column = m_unfoldedLines[cursor.m_line].m_colors.size() - 1;
|
||||
} else if (cursor.m_column > 0) {
|
||||
cursor.m_column--;
|
||||
}
|
||||
while (std::isblank(m_lines[cursor.m_line].m_colors[cursor.m_column]) && (cursor.m_line != 0 || cursor.m_column != 0)) {
|
||||
while (std::isblank(m_unfoldedLines[cursor.m_line].m_colors[cursor.m_column]) && (cursor.m_line != 0 || cursor.m_column != 0)) {
|
||||
if (cursor.m_column == 0 && cursor.m_line > 0) {
|
||||
cursor.m_line--;
|
||||
cursor.m_column = m_lines[cursor.m_line].m_colors.size() - 1;
|
||||
cursor.m_column = m_unfoldedLines[cursor.m_line].m_colors.size() - 1;
|
||||
} else
|
||||
cursor.m_column--;
|
||||
}
|
||||
if (m_lines[cursor.m_line].m_colors[cursor.m_column] == udt && (cursor.m_line != 0 || cursor.m_column != 0))
|
||||
if (m_unfoldedLines[cursor.m_line].m_colors[cursor.m_column] == udt && (cursor.m_line != 0 || cursor.m_column != 0))
|
||||
matchedBracket = true;
|
||||
}
|
||||
}
|
||||
@@ -379,17 +416,17 @@ namespace hex::ui {
|
||||
}
|
||||
if (currentIndex < line.size()) {
|
||||
Line::Flags flags(0);
|
||||
flags.m_value = m_lines[currentLine].m_flags[currentIndex];
|
||||
flags.m_value = m_unfoldedLines[currentLine].m_flags[currentIndex];
|
||||
flags.m_bits.preprocessor = withinPreproc;
|
||||
m_lines[currentLine].m_flags[currentIndex] = flags.m_value;
|
||||
m_unfoldedLines[currentLine].m_flags[currentIndex] = flags.m_value;
|
||||
}
|
||||
auto utf8CharLen = utf8CharLength(c);
|
||||
if (utf8CharLen > 1) {
|
||||
Line::Flags flags(0);
|
||||
flags.m_value = m_lines[currentLine].m_flags[currentIndex];
|
||||
flags.m_value = m_unfoldedLines[currentLine].m_flags[currentIndex];
|
||||
for (i32 j = 1; j < utf8CharLen; j++) {
|
||||
currentIndex++;
|
||||
m_lines[currentLine].m_flags[currentIndex] = flags.m_value;
|
||||
m_unfoldedLines[currentLine].m_flags[currentIndex] = flags.m_value;
|
||||
}
|
||||
}
|
||||
currentIndex++;
|
||||
@@ -401,7 +438,7 @@ namespace hex::ui {
|
||||
colorizeRange();
|
||||
}
|
||||
|
||||
void TextEditor::setLanguageDefinition(const LanguageDefinition &languageDef) {
|
||||
void TextEditor::Lines::setLanguageDefinition(const LanguageDefinition &languageDef) {
|
||||
m_languageDefinition = languageDef;
|
||||
m_regexList.clear();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,9 @@
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
|
||||
using Coordinates = TextEditor::Coordinates;
|
||||
|
||||
TextEditor::Line TextEditor::Line::trim(TrimMode trimMode) {
|
||||
if (m_chars.empty())
|
||||
return m_emptyLine;
|
||||
@@ -127,11 +130,11 @@ namespace hex::ui {
|
||||
return line.indexColumn(stringIndex);
|
||||
}
|
||||
|
||||
i32 TextEditor::lineMaxColumn(i32 lineIndex) {
|
||||
if (lineIndex >= (i64) m_lines.size() || lineIndex < 0)
|
||||
i32 TextEditor::Lines::lineMaxColumn(i32 lineIndex) {
|
||||
if (lineIndex >= (i64) size() || lineIndex < 0)
|
||||
return 0;
|
||||
|
||||
return m_lines[lineIndex].maxColumn();
|
||||
return operator[](lineIndex).maxColumn();
|
||||
}
|
||||
|
||||
// "Borrowed" from ImGui source
|
||||
@@ -174,15 +177,34 @@ namespace hex::ui {
|
||||
return size;
|
||||
}
|
||||
|
||||
TextEditor::Coordinates TextEditor::screenPosToCoordinates(const ImVec2 &position) {
|
||||
ImVec2 local = position - ImGui::GetCursorScreenPos();
|
||||
i32 lineNo = std::max(0, (i32) floor(local.y / m_charAdvance.y));
|
||||
if (lineNo >= (i32) m_lines.size())
|
||||
return setCoordinates((i32) m_lines.size() - 1, -1);
|
||||
else if (local.x < (m_leftMargin - 2_scaled) || m_lines[lineNo].empty())
|
||||
return setCoordinates(lineNo, 0);
|
||||
std::string line = m_lines[lineNo].m_chars;
|
||||
local.x -= (m_leftMargin - 5_scaled);
|
||||
Coordinates TextEditor::screenPosCoordinates(const ImVec2 &position) {
|
||||
auto boxSize = m_lines.m_charAdvance.x + (((u32)m_lines.m_charAdvance.x % 2) ? 2.0f : 1.0f);
|
||||
if (m_lines.isEmpty())
|
||||
return m_lines.lineCoordinates( 0, 0);
|
||||
auto lineSize = m_lines.size();
|
||||
if (position.y > m_lines.getLineStartScreenPos(0, lineIndexToRow(lineSize - 1)).y + m_lines.m_charAdvance.y)
|
||||
return m_lines.lineCoordinates( -1, -1);
|
||||
//if (position.y > m_lines.m_lineIndexToScreen[lineSize - 1].y + m_charAdvance.y)
|
||||
// return m_lines.lineCoordinates( -1, -1);
|
||||
|
||||
auto local = position - m_lines.m_cursorScreenPosition;
|
||||
auto row = screenPosToRow(position);
|
||||
|
||||
Coordinates result = lineCoordinates(0,0);
|
||||
i32 lineIndex= rowToLineIndex((i32) std::floor(row));
|
||||
if (lineIndex < 0 || lineIndex >= (i32) m_lines.size())
|
||||
return Invalid;
|
||||
result.m_line = lineIndex;
|
||||
if (m_lines.m_codeFoldKeyLineMap.contains(lineIndex) || m_lines.m_codeFoldValueLineMap.contains(lineIndex)) {
|
||||
if (local.x < (boxSize - 1)/2)
|
||||
return Invalid;
|
||||
}
|
||||
else if (local.x < 0 || m_lines[result.m_line].empty())
|
||||
return m_lines.lineCoordinates( result.m_line, 0);
|
||||
|
||||
|
||||
auto &line = m_lines[result.m_line].m_chars;
|
||||
//local.x -= (m_leftMargin - 5_scaled);
|
||||
i32 count = 0;
|
||||
u64 length;
|
||||
i32 increase;
|
||||
@@ -190,17 +212,17 @@ namespace hex::ui {
|
||||
increase = TextEditor::utf8CharLength(line[count]);
|
||||
count += increase;
|
||||
std::string partialLine = line.substr(0, count);
|
||||
length = ImGui::CalcTextSize(partialLine.c_str(), nullptr, false, m_charAdvance.x * count).x;
|
||||
length = ImGui::CalcTextSize(partialLine.c_str(), nullptr, false, m_lines.m_charAdvance.x * count).x;
|
||||
} while (length < local.x && count < (i32) line.size() + increase);
|
||||
|
||||
auto result = getCharacterCoordinates(lineNo, count - increase);
|
||||
result = setCoordinates(result);
|
||||
result = m_lines.lineIndexCoords(lineIndex + 1, count - increase);
|
||||
result = m_lines.foldedToUnfoldedCoords(result);
|
||||
if (result == Invalid)
|
||||
return {0, 0};
|
||||
return Coordinates(0, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
TextEditor::Coordinates TextEditor::lineCoordsToIndexCoords(const Coordinates &coordinates) const {
|
||||
Coordinates TextEditor::lineCoordsToIndexCoords(const Coordinates &coordinates) {
|
||||
if (coordinates.m_line >= (i64) m_lines.size())
|
||||
return Invalid;
|
||||
|
||||
@@ -208,38 +230,282 @@ namespace hex::ui {
|
||||
return {coordinates.m_line,line.columnIndex(coordinates.m_column)};
|
||||
}
|
||||
|
||||
i32 TextEditor::lineCoordinatesToIndex(const Coordinates &coordinates) const {
|
||||
if (coordinates.m_line >= (i64) m_lines.size())
|
||||
i32 TextEditor::Lines::lineCoordsIndex(const Coordinates &coordinates) {
|
||||
if (coordinates.m_line >= (i64) size())
|
||||
return -1;
|
||||
|
||||
const auto &line = m_lines[coordinates.m_line];
|
||||
auto &line = operator[](coordinates.m_line);
|
||||
return line.columnIndex(coordinates.m_column);
|
||||
}
|
||||
|
||||
TextEditor::Coordinates TextEditor::getCharacterCoordinates(i32 lineIndex, i32 strIndex) {
|
||||
if (lineIndex < 0 || lineIndex >= (i32) m_lines.size())
|
||||
return {0, 0};
|
||||
auto &line = m_lines[lineIndex];
|
||||
return setCoordinates(lineIndex, line.indexColumn(strIndex));
|
||||
Coordinates TextEditor::Lines::lineIndexCoords(i32 lineNumber, i32 stringIndex) {
|
||||
if (lineNumber < 1 || lineNumber > (i32) size())
|
||||
return lineCoordinates( 0, 0);
|
||||
auto &line = operator[](lineNumber - 1);
|
||||
return lineCoordinates(lineNumber - 1, line.indexColumn(stringIndex));
|
||||
}
|
||||
|
||||
u64 TextEditor::getLineByteCount(i32 lineIndex) const {
|
||||
if (lineIndex >= (i64) m_lines.size() || lineIndex < 0)
|
||||
return 0;
|
||||
std::vector<Coordinates> TextEditor::Lines::unfoldedEllipsisCoordinates(Range delimiterCoordinates) {
|
||||
|
||||
auto &line = m_lines[lineIndex];
|
||||
return line.size();
|
||||
auto lineStart = m_unfoldedLines[delimiterCoordinates.m_start.m_line];
|
||||
auto row = lineIndexToRow(delimiterCoordinates.m_start.m_line);
|
||||
float unfoldedSpan1, unfoldedSpan2, unfoldedSpan3;
|
||||
float unprocessedSpan1, unprocessedSpan2, unprocessedSpan3;
|
||||
bool adddsBothEnds = true;
|
||||
if (delimiterCoordinates.m_start.m_line == delimiterCoordinates.m_end.m_line) {
|
||||
unprocessedSpan1 = unfoldedSpan1 = delimiterCoordinates.m_end.m_column - delimiterCoordinates.m_start.m_column - 1;
|
||||
unprocessedSpan3 = unfoldedSpan3 = 0.0f;
|
||||
unprocessedSpan2 = unfoldedSpan2 = 0.0f;
|
||||
} else if (!m_foldedLines[row].addsFullFirstLineToFold() && !m_foldedLines[row].addsLastLineToFold()) {
|
||||
adddsBothEnds = false;
|
||||
auto innerLine = m_unfoldedLines[delimiterCoordinates.m_start.m_line];
|
||||
unprocessedSpan1 = unfoldedSpan1 = std::max(innerLine.maxColumn() - 1, 0);
|
||||
innerLine = m_unfoldedLines[delimiterCoordinates.m_end.m_line];
|
||||
unprocessedSpan3 = unfoldedSpan3 = std::max(innerLine.maxColumn() - 1, 0);
|
||||
unfoldedSpan2 = 0;
|
||||
for (i32 j = delimiterCoordinates.m_start.m_line + 1; j < delimiterCoordinates.m_end.m_line; j++) {
|
||||
innerLine = m_unfoldedLines[j];
|
||||
unfoldedSpan2 += innerLine.maxColumn();
|
||||
}
|
||||
unprocessedSpan2 = unfoldedSpan2;
|
||||
} else {
|
||||
unprocessedSpan1 = unfoldedSpan1 = std::max(lineStart.maxColumn() - delimiterCoordinates.m_start.m_column - 2, 0);
|
||||
unprocessedSpan3 = unfoldedSpan3 = std::max(delimiterCoordinates.m_end.m_column - 1, 0);
|
||||
unfoldedSpan2 = 0;
|
||||
for (i32 j = delimiterCoordinates.m_start.m_line + 1; j < delimiterCoordinates.m_end.m_line; j++) {
|
||||
auto innerLine = m_unfoldedLines[j];
|
||||
unfoldedSpan2 += innerLine.maxColumn();
|
||||
}
|
||||
unprocessedSpan2 = unfoldedSpan2;
|
||||
}
|
||||
|
||||
auto totalUnfoldedSpan = unfoldedSpan1 + unfoldedSpan2 + unfoldedSpan3;
|
||||
if (totalUnfoldedSpan < 2.0f) {
|
||||
std::vector<Coordinates> unfoldedEllipsisCoordinates(2);
|
||||
unfoldedEllipsisCoordinates[0] = lineCoordinates( delimiterCoordinates.m_start.m_line, delimiterCoordinates.m_start.m_column + 1);
|
||||
unfoldedEllipsisCoordinates[1] = delimiterCoordinates.m_end;
|
||||
return unfoldedEllipsisCoordinates;
|
||||
}
|
||||
|
||||
float spanFragment = totalUnfoldedSpan / 2.0f;
|
||||
|
||||
std::vector<Coordinates> unfoldedEllipsisCoordinates(4);
|
||||
if (adddsBothEnds) {
|
||||
unfoldedEllipsisCoordinates[0] = lineCoordinates(delimiterCoordinates.m_start.m_line, delimiterCoordinates.m_start.m_column + 1);
|
||||
unfoldedEllipsisCoordinates[3] = delimiterCoordinates.m_end;
|
||||
} else {
|
||||
unfoldedEllipsisCoordinates[0] = delimiterCoordinates.m_start;
|
||||
unfoldedEllipsisCoordinates[1] = lineCoordinates(delimiterCoordinates.m_start.m_line, delimiterCoordinates.m_start.m_column + 1);
|
||||
unfoldedEllipsisCoordinates[2] = delimiterCoordinates.m_end;
|
||||
unfoldedEllipsisCoordinates[3] = lineCoordinates(delimiterCoordinates.m_end.m_line, delimiterCoordinates.m_end.m_column + 1);
|
||||
return unfoldedEllipsisCoordinates;
|
||||
}
|
||||
|
||||
|
||||
i32 i = 1;
|
||||
while ((unprocessedSpan1 > spanFragment || std::fabs(unprocessedSpan1-spanFragment) < 0.001) && i < 3) {
|
||||
unfoldedEllipsisCoordinates[i] = lineCoordinates( unfoldedEllipsisCoordinates[0].m_line, 1 + unfoldedEllipsisCoordinates[0].m_column + std::round(i * spanFragment));
|
||||
unprocessedSpan1 -= spanFragment;
|
||||
i++;
|
||||
}
|
||||
auto leftOver = unprocessedSpan1;
|
||||
unprocessedSpan2 += leftOver;
|
||||
if ((unprocessedSpan2 > spanFragment || std::fabs(unprocessedSpan2 - spanFragment) < 0.001) && i < 3) {
|
||||
float lineLength = 0.0f;
|
||||
for (i32 j = delimiterCoordinates.m_start.m_line + 1; j < delimiterCoordinates.m_end.m_line; j++) {
|
||||
auto currentLineLength = (float) m_unfoldedLines[j].maxColumn();
|
||||
lineLength += currentLineLength + leftOver;
|
||||
leftOver = 0.0f;
|
||||
while ((lineLength > spanFragment || std::fabs(lineLength-spanFragment) < 0.001) && i < 3) {
|
||||
unfoldedEllipsisCoordinates[i] = lineCoordinates( j, std::round(currentLineLength - lineLength + spanFragment));
|
||||
unprocessedSpan2 -= spanFragment;
|
||||
lineLength -= spanFragment;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
unprocessedSpan3 += unprocessedSpan2;
|
||||
leftOver = unprocessedSpan2;
|
||||
auto firstI = i;
|
||||
while ((unprocessedSpan3 >= spanFragment || std::fabs(unprocessedSpan3-spanFragment) < 0.001) && i < 3) {
|
||||
unfoldedEllipsisCoordinates[i] = lineCoordinates( unfoldedEllipsisCoordinates[3].m_line, std::round((i - firstI + 1) * (spanFragment - leftOver)));
|
||||
unprocessedSpan3 -= spanFragment;
|
||||
i++;
|
||||
}
|
||||
|
||||
return unfoldedEllipsisCoordinates;
|
||||
}
|
||||
|
||||
TextEditor::Coordinates TextEditor::stringIndexToCoordinates(i32 strIndex, const std::string &input) {
|
||||
Coordinates TextEditor::Lines::foldedToUnfoldedCoords(const Coordinates &coords) {
|
||||
auto row = lineIndexToRow(coords.m_line);
|
||||
if (row == -1.0 || !m_foldedLines.contains(row))
|
||||
return coords;
|
||||
FoldedLine foldedLine = m_foldedLines[row];
|
||||
auto foldedSegments = foldedLine.m_foldedSegments;
|
||||
i32 foundIndex = -1;
|
||||
i32 loopLimit = (i32) 2 * foldedLine.m_keys.size();
|
||||
if (loopLimit == 0)
|
||||
return coords;
|
||||
Range::EndsInclusive endsInclusive = Range::EndsInclusive::Start;
|
||||
for (i32 i = 0; i <= loopLimit; i++) {
|
||||
if (Range(foldedSegments[i], foldedSegments[i + 1]).contains(coords, endsInclusive)) {
|
||||
foundIndex = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((i + 1) % 2)
|
||||
endsInclusive = Range::EndsInclusive::Both;
|
||||
else if ((i + 1) == loopLimit)
|
||||
endsInclusive = Range::EndsInclusive::End;
|
||||
else
|
||||
endsInclusive = Range::EndsInclusive::None;
|
||||
|
||||
}
|
||||
if (foundIndex < 0)
|
||||
return coords;
|
||||
|
||||
Range key = foldedLine.m_keys[(foundIndex / 2)-(foundIndex == loopLimit)];
|
||||
|
||||
if (foundIndex % 2) {
|
||||
|
||||
Range delimiterRange = foldedLine.findDelimiterCoordinates(key);
|
||||
std::vector<Coordinates> unfoldedEllipsisCoordinates = this->unfoldedEllipsisCoordinates(delimiterRange);
|
||||
|
||||
if (unfoldedEllipsisCoordinates.size() > 2)
|
||||
return unfoldedEllipsisCoordinates[coords.m_column - foldedLine.m_ellipsisIndices[foundIndex / 2]];
|
||||
else {
|
||||
auto ellipsisColumn = foldedLine.m_ellipsisIndices[foundIndex / 2];
|
||||
if (coords.m_column == ellipsisColumn || coords.m_column == ellipsisColumn + 2)
|
||||
return unfoldedEllipsisCoordinates[0];
|
||||
else
|
||||
return unfoldedEllipsisCoordinates[1];
|
||||
}
|
||||
} else {
|
||||
auto unfoldedSegmentStart = foldedLine.m_unfoldedSegments[foundIndex];
|
||||
auto foldedSegmentStart = foldedLine.m_foldedSegments[foundIndex];
|
||||
if (foundIndex == 0) {
|
||||
if (lineNeedsDelimiter(key.m_start.m_line)) {
|
||||
auto line = m_unfoldedLines[key.m_start.m_line];
|
||||
auto delimiterCoordinates = foldedLine.findDelimiterCoordinates(key);
|
||||
if (coords.m_column > line.maxColumn())
|
||||
return delimiterCoordinates.m_start;
|
||||
else
|
||||
return lineCoordinates( unfoldedSegmentStart.m_line, coords.m_column);
|
||||
}
|
||||
return lineCoordinates( unfoldedSegmentStart.m_line, coords.m_column);
|
||||
} else
|
||||
return lineCoordinates( unfoldedSegmentStart.m_line, coords.m_column - foldedSegmentStart.m_column + unfoldedSegmentStart.m_column);
|
||||
}
|
||||
}
|
||||
|
||||
Coordinates TextEditor::nextCoordinate(TextEditor::Coordinates coordinate) {
|
||||
auto line = m_lines[coordinate.m_line];
|
||||
if (line.isEndOfLine(coordinate.m_column))
|
||||
return Coordinates(coordinate.m_line + 1, 0);
|
||||
else
|
||||
return Coordinates(coordinate.m_line,coordinate.m_column + 1);
|
||||
}
|
||||
bool TextEditor::testfoldMaps(TextEditor::Range toTest) {
|
||||
bool result = true;
|
||||
for (auto test = toTest.getStart(); test <= toTest.getEnd(); test = nextCoordinate(test)) {
|
||||
auto data = test;
|
||||
auto folded = m_lines.unfoldedToFoldedCoords(data);
|
||||
auto unfolded = m_lines.foldedToUnfoldedCoords(folded);
|
||||
|
||||
result = result && (data == unfolded);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Coordinates TextEditor::Lines::unfoldedToFoldedCoords(const Coordinates &coords) {
|
||||
auto row = lineIndexToRow(coords.m_line);
|
||||
Coordinates result;
|
||||
if (row == -1 || !m_foldedLines.contains(row))
|
||||
return coords;
|
||||
|
||||
FoldedLine foldedLine = m_foldedLines[row];
|
||||
result.m_line = foldedLine.m_full.m_start.m_line;
|
||||
|
||||
i32 foundIndex = -1;
|
||||
i32 loopLimit = (i32) 2 * foldedLine.m_keys.size();
|
||||
|
||||
if (loopLimit == 0)
|
||||
return coords;
|
||||
Range::EndsInclusive endsInclusive = Range::EndsInclusive::Start;
|
||||
for (i32 i = 0; i <= loopLimit; i++) {
|
||||
Range range = Range(foldedLine.m_unfoldedSegments[i], foldedLine.m_unfoldedSegments[i + 1]);
|
||||
|
||||
if (range.contains(coords, endsInclusive)) {
|
||||
foundIndex = i;
|
||||
break;
|
||||
}
|
||||
if ((i + 1) % 2)
|
||||
endsInclusive = Range::EndsInclusive::Both;
|
||||
else if ((i + 1) == loopLimit)
|
||||
endsInclusive = Range::EndsInclusive::End;
|
||||
else
|
||||
endsInclusive = Range::EndsInclusive::None;
|
||||
}
|
||||
if (foundIndex < 0)
|
||||
return coords;
|
||||
|
||||
Range key = foldedLine.m_keys[(foundIndex / 2) - (foundIndex == loopLimit)];
|
||||
|
||||
if (foundIndex % 2) {
|
||||
result.m_column = foldedLine.m_ellipsisIndices[foundIndex / 2];
|
||||
Range delimiterRange = foldedLine.findDelimiterCoordinates(key);
|
||||
std::vector<Coordinates> unfoldedEllipsisCoordinates = this->unfoldedEllipsisCoordinates(delimiterRange);
|
||||
|
||||
if (unfoldedEllipsisCoordinates.size() > 2) {
|
||||
if (coords == unfoldedEllipsisCoordinates[0])
|
||||
return result;
|
||||
if (coords == unfoldedEllipsisCoordinates[3]) {
|
||||
result.m_column += 3;
|
||||
return result;
|
||||
}
|
||||
if (Range(unfoldedEllipsisCoordinates[0], unfoldedEllipsisCoordinates[1]).contains(coords, Range::EndsInclusive::End)) {
|
||||
result.m_column += 1;
|
||||
return result;
|
||||
}
|
||||
if (Range(unfoldedEllipsisCoordinates[1], unfoldedEllipsisCoordinates[2]).contains(coords, Range::EndsInclusive::End)) {
|
||||
result.m_column += 2;
|
||||
return result;
|
||||
}
|
||||
return coords;
|
||||
} else {
|
||||
if (coords > unfoldedEllipsisCoordinates[0])
|
||||
result.m_column += 3;
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
if (foundIndex == 0) {
|
||||
if (foldedLine.firstLineNeedsDelimiter()) {
|
||||
auto line = m_unfoldedLines[foldedLine.m_full.m_start.m_line];
|
||||
if (coords > lineCoordinates(foldedLine.m_full.m_start.m_line, line.maxColumn()))
|
||||
result.m_column = foldedLine.m_ellipsisIndices[0] - 1;
|
||||
else
|
||||
result.m_column = coords.m_column;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.m_column = coords.m_column;
|
||||
return result;
|
||||
} else
|
||||
result.m_column = coords.m_column - foldedLine.m_unfoldedSegments[foundIndex].m_column + foldedLine.m_foldedSegments[foundIndex].m_column;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Coordinates TextEditor::Lines::stringIndexCoords(i32 strIndex, const std::string &input) {
|
||||
if (strIndex < 0 || strIndex > (i32) input.size())
|
||||
return {0, 0};
|
||||
return lineCoordinates( 0, 0);
|
||||
std::string str = input.substr(0, strIndex);
|
||||
i32 line = std::count(str.begin(), str.end(), '\n');
|
||||
auto line = std::count(str.begin(), str.end(), '\n');
|
||||
auto index = str.find_last_of('\n');
|
||||
str = str.substr(index + 1);
|
||||
i32 col = TextEditor::stringCharacterCount(str);
|
||||
auto col = stringCharacterCount(str);
|
||||
|
||||
return {line, col};
|
||||
return lineCoordinates( line, col);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user