mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 15:57:03 -05:00
Compare commits
16 Commits
master
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b2e95b9d0 | ||
|
|
c677aae7b1 | ||
|
|
f70268ea40 | ||
|
|
d454714428 | ||
|
|
8b938faf01 | ||
|
|
324170e0d8 | ||
|
|
e463df9fc4 | ||
|
|
fe348d33e7 | ||
|
|
f9343c8e94 | ||
|
|
1670607e38 | ||
|
|
c92b55e1b6 | ||
|
|
77eff651ed | ||
|
|
219f588cbe | ||
|
|
0e2d7ee3bc | ||
|
|
b57d9118c1 | ||
|
|
d4879572fa |
@@ -1,31 +1,63 @@
|
||||
#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>
|
||||
|
||||
#include <utility>
|
||||
|
||||
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);
|
||||
bool operator<(const TokenInterval &other) const;
|
||||
bool operator>(const TokenInterval &other) const;
|
||||
bool operator==(const TokenInterval &other) const;
|
||||
bool operator!=(const TokenInterval &other) const;
|
||||
bool operator<=(const TokenInterval &other) const;
|
||||
bool operator>=(const TokenInterval &other) const;
|
||||
[[nodiscard]] bool contains(const TokenInterval &other) const;
|
||||
[[nodiscard]] bool contains(i32 value) const;
|
||||
[[nodiscard]] bool contiguous(const TokenInterval &other) const;
|
||||
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>;
|
||||
using StringVector = std::vector<std::string>;
|
||||
using StringSet = std::set<std::string>;
|
||||
using SafeTokenIterator = pl::hlp::SafeIterator<std::vector<Token>::const_iterator>;
|
||||
using VariableScopes = std::map<std::string,Scopes>;
|
||||
using Inheritances = std::map<std::string,VectorString>;
|
||||
using Inheritances = std::map<std::string,StringSet>;
|
||||
using IdentifierTypeColor = std::map<Identifier::IdentifierType,ui::TextEditor::PaletteIndex>;
|
||||
using TokenTypeColor = std::map<Token::Type,ui::TextEditor::PaletteIndex>;
|
||||
using TokenColor = std::map<i32, ui::TextEditor::PaletteIndex>;
|
||||
@@ -35,16 +67,18 @@ namespace hex::plugin::builtin {
|
||||
using CompileErrors = std::vector<CompileError>;
|
||||
using TokenSequence = std::vector<Token>;
|
||||
using TokenIdVector = std::vector<i32>;
|
||||
using TokenIdSet = std::set<i32>;
|
||||
using Instances = std::map<std::string,std::vector<i32>>;
|
||||
using CodeFoldBlocks = ui::TextEditor::CodeFoldBlocks;
|
||||
|
||||
struct ParentDefinition;
|
||||
struct Definition {
|
||||
Definition()= default;
|
||||
Definition(IdentifierType identifierType, std::string typeStr,i32 tokenId, Location location) : idType(identifierType), typeStr(typeStr), tokenIndex(tokenId),location(location) {}
|
||||
IdentifierType idType;
|
||||
Definition(IdentifierType identifierType, std::string typeStr,i32 tokenId, Location location) : idType(identifierType), typeStr(std::move(typeStr)), tokenIndex(tokenId),location(location) {}
|
||||
IdentifierType idType{};
|
||||
std::string typeStr;
|
||||
i32 tokenIndex;
|
||||
Location location;
|
||||
i32 tokenIndex{};
|
||||
Location location{};
|
||||
};
|
||||
|
||||
struct ParentDefinition {
|
||||
@@ -60,13 +94,13 @@ namespace hex::plugin::builtin {
|
||||
private:
|
||||
TextHighlighter *m_textHighlighter;
|
||||
Types definedTypes;
|
||||
VectorString usedNamespaces;
|
||||
StringVector usedNamespaces;
|
||||
ParsedImports parsedImports;
|
||||
Str2StrMap importedHeaders;
|
||||
TokenSequence fullTokens;
|
||||
std::string editedText;
|
||||
CompileErrors compileErrors;
|
||||
VectorString linesOfColors;
|
||||
StringVector linesOfColors;
|
||||
public:
|
||||
RequiredInputs() : m_textHighlighter(nullptr) {};
|
||||
explicit RequiredInputs(TextHighlighter *textHighlighter) : m_textHighlighter(textHighlighter) {}
|
||||
@@ -83,11 +117,12 @@ 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;
|
||||
StringVector m_lines;
|
||||
TokenIdVector m_firstTokenIdOfLine;
|
||||
ViewPatternEditor *m_viewPatternEditor;
|
||||
ViewPatternEditor *m_viewPatternEditor{};
|
||||
|
||||
|
||||
TokenColor m_tokenColors;
|
||||
@@ -111,52 +146,25 @@ namespace hex::plugin::builtin {
|
||||
Str2StrMap m_typeDefMap;
|
||||
Str2StrMap m_typeDefInvMap;
|
||||
|
||||
VectorString m_UDTs;
|
||||
std::set<i32> m_taggedIdentifiers;
|
||||
std::set<i32> m_memberChains;
|
||||
std::set<i32> m_scopeChains;
|
||||
StringVector m_UDTs;
|
||||
TokenIdSet m_taggedIdentifiers;
|
||||
TokenIdSet m_memberChains;
|
||||
TokenIdSet m_scopeChains;
|
||||
TokenIdSet m_identifierTokenIds;
|
||||
RequiredInputs m_requiredInputs;
|
||||
|
||||
TokenIter m_curr;
|
||||
TokenIter m_startToken, m_originalPosition, m_partOriginalPosition;
|
||||
SafeTokenIterator m_curr;
|
||||
SafeTokenIterator m_startToken, m_originalPosition, m_partOriginalPosition;
|
||||
|
||||
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;
|
||||
|
||||
@@ -166,6 +174,7 @@ namespace hex::plugin::builtin {
|
||||
ViewPatternEditor* getViewPatternEditor();
|
||||
void setViewPatternEditor(ViewPatternEditor *viewPatternEditor);
|
||||
|
||||
void setTokenIds();
|
||||
TextHighlighter();
|
||||
~TextHighlighter();
|
||||
explicit TextHighlighter(ViewPatternEditor *viewPatternEditor) : m_viewPatternEditor(viewPatternEditor) {}
|
||||
@@ -202,6 +211,7 @@ namespace hex::plugin::builtin {
|
||||
/// 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 +263,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);
|
||||
@@ -264,8 +274,8 @@ namespace hex::plugin::builtin {
|
||||
bool findIdentifierDefinition(Definition &result, const std::string &optionalIdentifierName = "", std::string optionalName = "", bool optional = false);
|
||||
/// To deal with the Parent keyword
|
||||
std::optional<Definition> setChildrenTypes();
|
||||
bool findParentTypes(VectorString &parentTypes, const std::string &optionalName="");
|
||||
bool findAllParentTypes(VectorString &parentTypes, std::vector<Identifier *> &identifiers, std::string &optionalFullName);
|
||||
bool findParentTypes(StringVector &parentTypes, const std::string &optionalName="");
|
||||
bool findAllParentTypes(StringVector &parentTypes, std::vector<Identifier *> &identifiers, std::string &optionalFullName);
|
||||
bool tryParentType(const std::string &parentType, std::string &variableName, std::optional<Definition> &result, std::vector<Identifier *> &identifiers);
|
||||
/// Convenience function
|
||||
bool isTokenIdValid(i32 tokenId);
|
||||
@@ -278,6 +288,8 @@ namespace hex::plugin::builtin {
|
||||
pl::core::Location getLocation(i32 tokenId);
|
||||
/// Calculate the token index of a source code, line and column numbers
|
||||
i32 getTokenId(pl::core::Location location);
|
||||
i32 getTokenId(SafeTokenIterator tokenIterator);
|
||||
i32 getTokenId();
|
||||
/// Calculate the function or template argument position from token indices
|
||||
i32 getArgumentNumber(i32 start,i32 arg);
|
||||
/// Calculate the token index of a function or template argument position
|
||||
@@ -288,18 +300,18 @@ namespace hex::plugin::builtin {
|
||||
|
||||
/// The following functions were copied from the parser and some were modified
|
||||
|
||||
template<typename T> T *getValue(const i32 index);
|
||||
template<typename T> T *getValue(i32 index);
|
||||
void next(i32 count = 1);
|
||||
bool begin();
|
||||
void partBegin();
|
||||
void reset();
|
||||
void partReset();
|
||||
bool resetIfFailed(const bool value) ;
|
||||
bool resetIfFailed(bool value);
|
||||
template<auto S = Normal> bool sequenceImpl();
|
||||
template<auto S = Normal> bool matchOne(const Token &token);
|
||||
template<auto S = Normal> bool sequenceImpl(const auto &... args);
|
||||
template<auto S = Normal> bool sequence(const Token &token, const auto &... args);
|
||||
bool isValid();
|
||||
bool peek(const Token &token, const i32 index = 0);
|
||||
bool peek(const Token &token, i32 index = 0);
|
||||
};
|
||||
}
|
||||
@@ -60,7 +60,7 @@ namespace hex::plugin::builtin {
|
||||
[[nodiscard]] bool isPinned() const { return m_isPinned; }
|
||||
void setPinned(const bool pinned) { m_isPinned = pinned; }
|
||||
|
||||
[[nodiscard]] virtual ImGuiWindowFlags getFlags() const { return ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse; }
|
||||
[[nodiscard]] virtual ImGuiWindowFlags getFlags() const { return ImGuiWindowFlags_AlwaysAutoResize; }
|
||||
|
||||
private:
|
||||
bool m_isPinned = false;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace hex::plugin::builtin {
|
||||
std::string& get(prv::Provider *provider);
|
||||
[[nodiscard]] bool hasProviderSpecificSource(prv::Provider *provider) const;
|
||||
|
||||
bool isSynced() const;
|
||||
[[nodiscard]] bool isSynced() const;
|
||||
void enableSync(bool enabled);
|
||||
|
||||
private:
|
||||
@@ -41,18 +41,9 @@ namespace hex::plugin::builtin {
|
||||
void drawAlwaysVisibleContent() override;
|
||||
std::unique_ptr<pl::PatternLanguage> *getPatternLanguage() { return &m_editorRuntime; }
|
||||
ui::TextEditor *getTextEditor();
|
||||
u32 getRunningParsers () const { return m_runningParsers;}
|
||||
u32 getRunningEvaluators () const { return m_runningEvaluators;}
|
||||
u32 getRunningHighlighters () const { return m_runningHighlighters;}
|
||||
void incrementRunningParsers( i32 amount) { m_runningParsers += amount; }
|
||||
void incrementRunningEvaluators( i32 amount) { m_runningEvaluators += amount; }
|
||||
void incrementRunningHighlighters( i32 amount) { m_runningHighlighters += amount; }
|
||||
bool hasUnevaluatedChanges(prv::Provider *provider) const;
|
||||
void setChangesWereParsed(bool changesWereParsed) { m_changesWereParsed = changesWereParsed;}
|
||||
void setChangesWereColored(bool changesWereColored) { m_changesWereColored = changesWereColored; }
|
||||
void drawContent() override;
|
||||
void setPopupWindowHeight(u32 height) { m_popupWindowHeight = height; }
|
||||
u32 getPopupWindowHeight() const { return m_popupWindowHeight; }
|
||||
enum class DangerousFunctionPerms : u8 { Ask, Allow, Deny };
|
||||
|
||||
void drawHelpText() override;
|
||||
@@ -64,14 +55,14 @@ namespace hex::plugin::builtin {
|
||||
class PopupAcceptPattern;
|
||||
|
||||
struct PatternVariable {
|
||||
bool inVariable;
|
||||
bool outVariable;
|
||||
bool inVariable{};
|
||||
bool outVariable{};
|
||||
|
||||
pl::core::Token::ValueType type;
|
||||
pl::core::Token::ValueType type{};
|
||||
pl::core::Token::Literal value;
|
||||
};
|
||||
|
||||
enum class EnvVarType
|
||||
enum class EnvVarType : u8
|
||||
{
|
||||
Integer,
|
||||
Float,
|
||||
@@ -137,15 +128,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
std::mutex m_logMutex;
|
||||
|
||||
PerProvider<ui::TextEditor::Coordinates> m_cursorPosition;
|
||||
PerProvider<ImVec2> m_scroll;
|
||||
PerProvider<ImVec2> m_consoleScroll;
|
||||
|
||||
PerProvider<ui::TextEditor::Coordinates> m_consoleCursorPosition;
|
||||
PerProvider<ui::TextEditor::Range> m_selection;
|
||||
PerProvider<ui::TextEditor::Range> m_consoleSelection;
|
||||
PerProvider<size_t> m_consoleLongestLineLength;
|
||||
PerProvider<ui::TextEditor::Breakpoints> m_breakpoints;
|
||||
PerProvider<std::optional<pl::core::err::PatternLanguageError>> m_lastEvaluationError;
|
||||
PerProvider<std::vector<pl::core::err::CompileError>> m_lastCompileError;
|
||||
PerProvider<const std::vector<pl::core::Evaluator::StackTrace>*> m_callStack;
|
||||
@@ -168,7 +152,6 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Settings::SettingsVariable<bool, "hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.pattern_parent_highlighting"> m_parentHighlightingEnabled = false; bool m_replaceMode = false;
|
||||
bool m_openFindReplacePopUp = false;
|
||||
bool m_openGotoLinePopUp = false;
|
||||
bool m_patternEvaluating = false;
|
||||
std::map<std::fs::path, std::string> m_patternNames;
|
||||
PerProvider<wolv::io::ChangeTracker> m_changeTracker;
|
||||
PerProvider<bool> m_ignoreNextChangeEvent;
|
||||
|
||||
@@ -1125,6 +1125,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",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -1287,11 +1289,11 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::BeginChild("##debugger", size, true)) {
|
||||
auto &evaluator = runtime.getInternals().evaluator;
|
||||
m_breakpoints = m_textEditor.get(provider).getBreakpoints();
|
||||
evaluator->setBreakpoints(m_breakpoints);
|
||||
ui::TextEditor::Breakpoints breakpoints = m_textEditor.get(provider).getBreakpoints();
|
||||
evaluator->setBreakpoints(breakpoints);
|
||||
|
||||
m_breakpoints = evaluator->getBreakpoints();
|
||||
m_textEditor.get(provider).setBreakpoints(m_breakpoints);
|
||||
breakpoints = evaluator->getBreakpoints();
|
||||
m_textEditor.get(provider).setBreakpoints(breakpoints);
|
||||
|
||||
if (*m_breakpointHit) {
|
||||
auto displayValue = [&](const auto &parent, size_t index) {
|
||||
@@ -1429,12 +1431,12 @@ namespace hex::plugin::builtin {
|
||||
|
||||
{
|
||||
if (m_textEditor.get(provider).isBreakpointsChanged()) {
|
||||
m_breakpoints = m_textEditor.get(provider).getBreakpoints();
|
||||
ui::TextEditor::Breakpoints breakpoints = m_textEditor.get(provider).getBreakpoints();
|
||||
m_textEditor.get(provider).clearBreakpointsChanged();
|
||||
const auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||
auto &evaluator = runtime.getInternals().evaluator;
|
||||
if (evaluator) {
|
||||
evaluator->setBreakpoints(m_breakpoints);
|
||||
evaluator->setBreakpoints(breakpoints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1474,6 +1476,8 @@ 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().setAllCodeFolds();
|
||||
m_textEditor.get(provider).getLines().applyCodeFoldStates();
|
||||
m_allStepsCompleted = true;
|
||||
}
|
||||
|
||||
@@ -1639,7 +1643,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
this->evaluatePattern(code, provider);
|
||||
m_textEditor.get(provider).setText(code, true);
|
||||
m_sourceCode.get(provider) = code;
|
||||
m_textEditor.get(provider).removeHiddenLinesFromPattern();
|
||||
m_sourceCode.get(provider) = m_textEditor.get(provider).getText();
|
||||
if (trackFile) {
|
||||
m_changeTracker.get(provider) = wolv::io::ChangeTracker(file);
|
||||
m_changeTracker.get(provider).startTracking([this, provider]{ this->handleFileChange(provider); });
|
||||
@@ -1653,7 +1658,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ContentRegistry::PatternLanguage::configureRuntime(*m_editorRuntime, nullptr);
|
||||
const auto &ast = m_editorRuntime->parseString(code, pl::api::Source::DefaultSource);
|
||||
m_textEditor.get(provider).setLongestLineLength(m_editorRuntime->getInternals().preprocessor.get()->getLongestLineLength());
|
||||
m_textEditor.get(provider).setLongestLineLength(m_editorRuntime->getInternals().preprocessor->getLongestLineLength());
|
||||
|
||||
auto &patternVariables = m_patternVariables.get(provider);
|
||||
auto oldPatternVariables = std::move(patternVariables);
|
||||
@@ -1704,7 +1709,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
m_consoleEditor.get(provider).clearActionables();
|
||||
m_console.get(provider).clear();
|
||||
m_consoleLongestLineLength.get(provider) = 0;
|
||||
m_consoleEditor.get(provider).setLongestLineLength(0);
|
||||
m_consoleNeedsUpdate = true;
|
||||
|
||||
m_consoleEditor.get(provider).setText("");
|
||||
@@ -1777,8 +1782,7 @@ namespace hex::plugin::builtin {
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (m_consoleLongestLineLength.get(provider) < line.size()) {
|
||||
m_consoleLongestLineLength.get(provider) = line.size();
|
||||
if (m_consoleEditor.get(provider).getLongestLineLength() < line.size()) {
|
||||
m_consoleEditor.get(provider).setLongestLineLength(line.size());
|
||||
}
|
||||
m_console.get(provider).emplace_back(line);
|
||||
@@ -1836,6 +1840,7 @@ namespace hex::plugin::builtin {
|
||||
return;
|
||||
|
||||
m_textEditor.get(provider).setText(wolv::util::preprocessText(code));
|
||||
m_textEditor.get(provider).removeHiddenLinesFromPattern();
|
||||
m_sourceCode.get(provider) = code;
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
});
|
||||
@@ -1847,12 +1852,18 @@ namespace hex::plugin::builtin {
|
||||
EventProviderOpened::subscribe(this, [this](prv::Provider *provider) {
|
||||
m_textEditor.get(provider).setLanguageDefinition(PatternLanguage());
|
||||
m_textEditor.get(provider).setShowWhitespaces(false);
|
||||
m_textEditor.get(provider).setCursorPosition(ui::TextEditor::Coordinates(0, 0),false,false);
|
||||
//if (getLastFocusedView() == this) {
|
||||
// m_textEditor.get(provider).setFocus(true);
|
||||
//}
|
||||
|
||||
|
||||
m_consoleEditor.get(provider).setLanguageDefinition(ConsoleLog());
|
||||
m_consoleEditor.get(provider).setShowWhitespaces(false);
|
||||
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: ";
|
||||
@@ -1864,37 +1875,26 @@ namespace hex::plugin::builtin {
|
||||
m_envVarEntries.get(provider).emplace_back(0, "", i128(0), EnvVarType::Integer);
|
||||
|
||||
m_debuggerDrawer.get(provider) = std::make_unique<ui::PatternDrawer>();
|
||||
m_cursorPosition.get(provider) = ui::TextEditor::Coordinates(0, 0);
|
||||
});
|
||||
|
||||
EventProviderChanged::subscribe(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
|
||||
if (oldProvider != nullptr) {
|
||||
m_sourceCode.get(oldProvider) = m_textEditor.get(oldProvider).getText();
|
||||
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();
|
||||
m_breakpoints.get(oldProvider) = m_textEditor.get(oldProvider).getBreakpoints();
|
||||
m_consoleCursorPosition.get(oldProvider) = m_consoleEditor.get(oldProvider).getCursorPosition();
|
||||
m_consoleSelection.get(oldProvider) = m_consoleEditor.get(oldProvider).getSelection();
|
||||
m_consoleLongestLineLength.get(oldProvider) = m_consoleEditor.get(oldProvider).getLongestLineLength();
|
||||
m_consoleScroll.get(oldProvider) = m_consoleEditor.get(oldProvider).getScroll();
|
||||
}
|
||||
|
||||
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).setScroll(m_scroll.get(newProvider));
|
||||
m_textEditor.get(newProvider).setSelection(m_selection.get(newProvider));
|
||||
m_textEditor.get(newProvider).setBreakpoints(m_breakpoints.get(newProvider));
|
||||
m_textEditor.get(newProvider).setTextChanged(false);
|
||||
m_hasUnevaluatedChanges.get(newProvider) = true;
|
||||
m_consoleEditor.get(newProvider).setText(wolv::util::combineStrings(m_console.get(newProvider), "\n"));
|
||||
m_consoleEditor.get(newProvider).setCursorPosition(m_consoleCursorPosition.get(newProvider));
|
||||
m_consoleEditor.get(newProvider).setLongestLineLength(m_consoleLongestLineLength.get(newProvider));
|
||||
m_consoleEditor.get(newProvider).setSelection(m_consoleSelection.get(newProvider));
|
||||
m_consoleEditor.get(newProvider).setScroll(m_consoleScroll.get(newProvider));
|
||||
|
||||
}
|
||||
//if (getLastFocusedView() == this) {
|
||||
// m_textEditor.get(newProvider).setFocus(false);
|
||||
//}
|
||||
|
||||
});
|
||||
|
||||
@@ -2126,16 +2126,16 @@ namespace hex::plugin::builtin {
|
||||
const auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||
|
||||
auto &evaluator = runtime.getInternals().evaluator;
|
||||
m_breakpoints = m_textEditor.get(ImHexApi::Provider::get()).getBreakpoints();
|
||||
evaluator->setBreakpoints(m_breakpoints);
|
||||
ui::TextEditor::Breakpoints breakpoints = m_textEditor.get(ImHexApi::Provider::get()).getBreakpoints();
|
||||
evaluator->setBreakpoints(breakpoints);
|
||||
|
||||
if (m_breakpoints->contains(line))
|
||||
if (breakpoints.contains(line))
|
||||
evaluator->removeBreakpoint(line);
|
||||
else
|
||||
evaluator->addBreakpoint(line);
|
||||
|
||||
m_breakpoints = evaluator->getBreakpoints();
|
||||
m_textEditor.get(ImHexApi::Provider::get()).setBreakpoints(m_breakpoints);
|
||||
breakpoints = evaluator->getBreakpoints();
|
||||
m_textEditor.get(ImHexApi::Provider::get()).setBreakpoints(breakpoints);
|
||||
}, [] { return ImHexApi::Provider::isValid(); },
|
||||
this);
|
||||
|
||||
@@ -2332,20 +2332,17 @@ namespace hex::plugin::builtin {
|
||||
.required = false,
|
||||
.load = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) {
|
||||
const auto sourceCode = wolv::util::preprocessText(tar.readString(basePath));
|
||||
|
||||
m_sourceCode.get(provider) = sourceCode;
|
||||
|
||||
if (provider == ImHexApi::Provider::get())
|
||||
m_textEditor.get(provider).setText(sourceCode);
|
||||
m_textEditor.get(provider).setText(sourceCode);
|
||||
m_textEditor.get(provider).removeHiddenLinesFromPattern();
|
||||
m_sourceCode.get(provider) = m_textEditor.get(provider).getText();
|
||||
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
return true;
|
||||
},
|
||||
.store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) {
|
||||
if (provider == ImHexApi::Provider::get())
|
||||
m_sourceCode.get(provider) = m_textEditor.get(provider).getText();
|
||||
m_sourceCode.get(provider) = m_textEditor.get(provider).getText();
|
||||
|
||||
const auto &sourceCode = m_sourceCode.get(provider);
|
||||
auto sourceCode = m_textEditor.get(provider).getText(true);
|
||||
|
||||
tar.writeString(basePath, wolv::util::trim(sourceCode));
|
||||
return true;
|
||||
@@ -2535,7 +2532,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
|
||||
@@ -2564,12 +2591,12 @@ namespace hex::plugin::builtin {
|
||||
|
||||
// Wait until evaluation has finished
|
||||
while (m_runningEvaluators > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100ll));
|
||||
}
|
||||
|
||||
auto lock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||
|
||||
auto evaluationResult = m_lastEvaluationResult.load();
|
||||
int evaluationResult = m_lastEvaluationResult.load();
|
||||
|
||||
nlohmann::json result = {
|
||||
{ "handle", provider->getID() },
|
||||
@@ -2588,7 +2615,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
// Wait until evaluation has finished
|
||||
while (m_runningEvaluators > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100ll));
|
||||
}
|
||||
|
||||
auto lock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||
@@ -2612,7 +2639,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
// Wait until evaluation has finished
|
||||
while (m_runningEvaluators > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100ll));
|
||||
}
|
||||
|
||||
auto lock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||
@@ -2644,6 +2671,7 @@ namespace hex::plugin::builtin {
|
||||
m_changeEventAcknowledgementPending.get(provider) = true;
|
||||
hex::ui::BannerButton::open(ICON_VS_INFO, "hex.builtin.provider.file.reload_changes", ImColor(66, 104, 135), "hex.builtin.provider.file.reload_changes.reload", [this, provider] {
|
||||
m_changeEventAcknowledgementPending.get(provider) = false;
|
||||
loadPatternFile(m_changeTracker.get(provider).getPath(), provider, true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2706,7 +2734,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 +2757,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
864
plugins/ui/source/ui/text_editor/codeFolder.cpp
Normal file
864
plugins/ui/source/ui/text_editor/codeFolder.cpp
Normal file
@@ -0,0 +1,864 @@
|
||||
#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 <hex/api/content_registry/pattern_language.hpp>
|
||||
#include <pl/core/preprocessor.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;
|
||||
using RangeFromCoordinates = TextEditor::RangeFromCoordinates;
|
||||
using CodeFoldState = TextEditor::CodeFoldState;
|
||||
|
||||
void TextEditor::Lines::skipAttribute() {
|
||||
|
||||
if (sequence(tkn::Separator::LeftBracket, tkn::Separator::LeftBracket)) {
|
||||
while (!sequence(tkn::Separator::RightBracket, tkn::Separator::RightBracket))
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
Interval TextEditor::Lines::findBlockInRange(Interval interval) {
|
||||
Interval result = NotValid;
|
||||
auto tokenStart = SafeTokenIterator(m_tokens.begin(), m_tokens.end());
|
||||
|
||||
bool foundKeyword = false;
|
||||
bool foundComment = false;
|
||||
m_curr = tokenStart + interval.m_start;
|
||||
while (interval.m_end >= getTokenId()) {
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return NotValid;
|
||||
if (result.m_start = getTokenId(); result.m_start < 0)
|
||||
return NotValid;
|
||||
|
||||
while (true) {
|
||||
if (const auto *docComment = const_cast<Token::DocComment *>(getValue<Token::DocComment>(0)); docComment != nullptr && getTokenId() == m_firstTokenIdOfLine.at(m_curr->location.line - 1)) {
|
||||
if (foundKeyword)
|
||||
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 && getTokenId() == m_firstTokenIdOfLine.at(m_curr->location.line - 1)) {
|
||||
if (foundKeyword)
|
||||
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 && getTokenId() == m_firstTokenIdOfLine.at(m_curr->location.line - 1)) {
|
||||
if (foundComment)
|
||||
break;
|
||||
foundKeyword = true;
|
||||
i32 lineIndex = m_curr->location.line - 1;
|
||||
auto nextIndex = nextLineIndex(lineIndex);
|
||||
if (nextIndex == lineIndex || nextIndex == -1)
|
||||
break;
|
||||
auto tokenId = m_firstTokenIdOfLine.at(nextIndex);
|
||||
next(tokenId - getTokenId());
|
||||
} else if (const auto *directive = const_cast<Token::Directive *>(getValue<Token::Directive>(0));directive != nullptr && *directive == Token::Directive::Include && getTokenId() == m_firstTokenIdOfLine.at(m_curr->location.line - 1)) {
|
||||
if (foundComment)
|
||||
break;
|
||||
foundKeyword = true;
|
||||
i32 lineIndex = m_curr->location.line - 1;
|
||||
auto nextIndex = nextLineIndex(lineIndex);
|
||||
if (nextIndex == lineIndex || nextIndex == -1)
|
||||
break;
|
||||
auto tokenId = m_firstTokenIdOfLine.at(nextIndex);
|
||||
next(tokenId - getTokenId());
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (foundKeyword || foundComment) {
|
||||
auto currentId = getTokenId();
|
||||
if (peek(tkn::Separator::EndOfProgram) || (currentId > 0 && currentId < (i32) m_tokens.size())) {
|
||||
next(-1);
|
||||
if (result.m_end = getTokenId(); 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 = SafeTokenIterator(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 {(i32) (location.line - 1), (i32) (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 {(i32)(location.line + lineCount - 2), (i32)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 {(i32)(location.line - 1), (i32)(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 {(i32)(location.line + lineCount - 2), (i32)endColumn};
|
||||
}
|
||||
}
|
||||
m_curr = save;
|
||||
return result;
|
||||
}
|
||||
|
||||
//comments imports and includes
|
||||
void TextEditor::Lines::nonDelimitedFolds() {
|
||||
auto size = m_tokens.size();
|
||||
if (size > 0) {
|
||||
Interval block = {0,static_cast<i32>(size-1)};
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RangeFromCoordinates TextEditor::Lines::getDelimiterLineNumbers(i32 start, i32 end, const std::string &delimiters) {
|
||||
RangeFromCoordinates result = {Invalid, Invalid};
|
||||
Coordinates first = Invalid;
|
||||
auto tokenStart = SafeTokenIterator(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 = Token::Operator{};
|
||||
Token::Operator closeOperator = Token::Operator{};
|
||||
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() == 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) {
|
||||
i32 tempLineIndex;
|
||||
if (tempLineIndex = nextLineIndex(lineIndex); lineIndex == tempLineIndex) {
|
||||
lineIndex++;
|
||||
currentTokenId = -1;
|
||||
location = Location::Empty();
|
||||
return;
|
||||
}
|
||||
lineIndex = tempLineIndex;
|
||||
currentTokenId = m_firstTokenIdOfLine[lineIndex];
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
location = m_curr->location;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::incrementTokenId(i32 &lineIndex, i32 ¤tTokenId, Location &location) {
|
||||
currentTokenId++;
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::moveToStringIndex(i32 stringIndex, i32 ¤tTokenId, Location &location) {
|
||||
auto curr = m_curr;
|
||||
i32 tempTokenId;
|
||||
auto &line = operator[](location.line - 1);
|
||||
while (stringIndex > line.columnIndex(m_curr->location.column) + (i32) m_curr->location.length && m_curr->location.line == location.line)
|
||||
next();
|
||||
if (peek(tkn::Separator::EndOfProgram))
|
||||
return;
|
||||
|
||||
if (tempTokenId = getTokenId(); tempTokenId < 0) {
|
||||
m_curr = curr;
|
||||
location = curr->location;
|
||||
} else {
|
||||
location = m_curr->location;
|
||||
currentTokenId = tempTokenId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TextEditor::Lines::resetToTokenId(i32 &lineIndex, i32 ¤tTokenId, Location &location) {
|
||||
m_curr = m_startToken + currentTokenId;
|
||||
location = m_curr->location;
|
||||
lineIndex = location.line - 1;
|
||||
}
|
||||
|
||||
i32 TextEditor::Lines::findNextDelimiter(bool openOnly) {
|
||||
while (!peek(tkn::Separator::EndOfProgram)) {
|
||||
|
||||
if (peek(tkn::Separator::LeftBrace) || peek(tkn::Separator::LeftBracket) || peek(tkn::Separator::LeftParenthesis) || peek(tkn::Operator::BoolLessThan))
|
||||
return getTokenId();
|
||||
if (!openOnly) {
|
||||
if (peek(tkn::Separator::RightBrace) || peek(tkn::Separator::RightBracket) || peek(tkn::Separator::RightParenthesis) || peek(tkn::Operator::BoolGreaterThan))
|
||||
return getTokenId();
|
||||
}
|
||||
next();
|
||||
}
|
||||
return getTokenId();
|
||||
}
|
||||
|
||||
TextEditor::CodeFoldBlocks TextEditor::Lines::foldPointsFromSource() {
|
||||
auto code = getText();
|
||||
if (code.empty())
|
||||
return m_foldPoints;
|
||||
std::unique_ptr<pl::PatternLanguage> runtime = std::make_unique<pl::PatternLanguage>();
|
||||
ContentRegistry::PatternLanguage::configureRuntime(*runtime, nullptr);
|
||||
std::ignore = runtime->preprocessString(code, pl::api::Source::DefaultSource);
|
||||
m_tokens = runtime->getInternals().preprocessor->getResult();
|
||||
const u32 tokenCount = m_tokens.size();
|
||||
if (tokenCount == 0)
|
||||
return m_foldPoints;
|
||||
loadFirstTokenIdOfLine();
|
||||
if (m_firstTokenIdOfLine.empty())
|
||||
return m_foldPoints;
|
||||
m_foldPoints.clear();
|
||||
nonDelimitedFolds();
|
||||
std::string openDelimiters = "{[(<";
|
||||
size_t topLine = 0;
|
||||
m_startToken = SafeTokenIterator(m_tokens.begin(), m_tokens.end());
|
||||
m_curr = m_startToken;
|
||||
auto location = m_curr->location;
|
||||
i32 lineIndex = topLine;
|
||||
i32 currentTokenId = 0;
|
||||
while (currentTokenId < (i32) tokenCount) {
|
||||
|
||||
if (currentTokenId = findNextDelimiter(true); !peek(tkn::Separator::EndOfProgram)) {
|
||||
if (currentTokenId < 0) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
auto line = operator[](m_curr->location.line - 1);
|
||||
size_t stringIndex = line.columnIndex(m_curr->location.column);
|
||||
std::string openDelimiter = std::string(1, line[static_cast<u64>(stringIndex - 1)]);
|
||||
location = m_curr->location;
|
||||
|
||||
if (auto idx = openDelimiters.find(openDelimiter); idx != std::string::npos) {
|
||||
if (idx == 3) {
|
||||
if (currentTokenId == 0) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
next(-1);
|
||||
auto column = m_curr[0].location.column - 1;
|
||||
if (line.m_colors[column] != (char) PaletteIndex::UserDefinedType) {
|
||||
next(2);
|
||||
if (peek(tkn::Separator::EndOfProgram, -1)) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
|
||||
if (currentTokenId = getTokenId(); 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_startToken + end.first); currentTokenId < 0 || currentTokenId >= (i32) m_tokens.size()) {
|
||||
return m_foldPoints;
|
||||
}
|
||||
incrementTokenId(lineIndex, currentTokenId, location);
|
||||
} else {
|
||||
return m_foldPoints;
|
||||
}
|
||||
} else {
|
||||
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 = SafeTokenIterator(m_tokens.begin(), m_tokens.end());
|
||||
const i32 tokenCount = m_tokens.size();
|
||||
if (from >= tokenCount)
|
||||
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[static_cast<u64>(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;
|
||||
i32 lineIndex = location.line - 1;
|
||||
while (currentTokenId < tokenCount) {
|
||||
|
||||
if (currentTokenId = findNextDelimiter(false); !peek(tkn::Separator::EndOfProgram)) {
|
||||
if (currentTokenId < 0) {
|
||||
return result;
|
||||
}
|
||||
line = operator[](m_curr->location.line - 1);
|
||||
std::string currentChar = std::string(1, line[(u64)(m_curr->location.column - 1)]);
|
||||
location = m_curr->location;
|
||||
|
||||
if (auto idx = blockDelimiters.find(currentChar); idx != std::string::npos) {
|
||||
if (currentChar == closeDelimiter) {
|
||||
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(); currentTokenId < 0)
|
||||
return result;
|
||||
|
||||
resetToTokenId(lineIndex, currentTokenId, location);
|
||||
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(tokenStart + end.first); currentTokenId < 0 || currentTokenId >= (i32) m_tokens.size())
|
||||
return result;
|
||||
|
||||
incrementTokenId(lineIndex, currentTokenId, location);
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextEditor::saveCodeFoldStates() {
|
||||
m_lines.saveCodeFoldStates();
|
||||
}
|
||||
|
||||
void TextEditor::Lines::saveCodeFoldStates() {
|
||||
i32 codeFoldIndex = 0;
|
||||
Indices closedFoldIncrements;
|
||||
for (auto key: m_codeFoldKeys) {
|
||||
if (m_codeFoldState.contains(key) && !m_codeFoldState[key]) {
|
||||
closedFoldIncrements.push_back(codeFoldIndex);
|
||||
codeFoldIndex = 1;
|
||||
} else
|
||||
codeFoldIndex++;
|
||||
}
|
||||
if (!m_hiddenLines.empty())
|
||||
m_hiddenLines.clear();
|
||||
if (!closedFoldIncrements.empty()) {
|
||||
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;
|
||||
m_hiddenLines.emplace_back(lineIndex, result);
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::applyCodeFoldStates() {
|
||||
m_lines.applyCodeFoldStates();
|
||||
}
|
||||
|
||||
void TextEditor::Lines::setCodeFoldState(CodeFoldState state) {
|
||||
m_codeFoldState = state;
|
||||
saveCodeFoldStates();
|
||||
}
|
||||
|
||||
CodeFoldState TextEditor::Lines::getCodeFoldState() const {
|
||||
return m_codeFoldState;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::resetCodeFoldStates() {
|
||||
m_codeFoldState.clear();
|
||||
for (auto key: m_codeFoldKeys)
|
||||
m_codeFoldState[key] = true;
|
||||
}
|
||||
|
||||
void TextEditor::Lines::applyCodeFoldStates() {
|
||||
|
||||
std::string commentLine;
|
||||
for (const auto& line: m_hiddenLines) {
|
||||
if (line.m_line.starts_with("//+-#:")) {
|
||||
commentLine = line.m_line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (commentLine.size() < 6 || !commentLine.starts_with("//+-#:")) {
|
||||
resetCodeFoldStates();
|
||||
return;
|
||||
}
|
||||
auto states = commentLine.substr(6);
|
||||
i32 count;
|
||||
StringVector stringVector;
|
||||
if (states.empty())
|
||||
count = 0;
|
||||
else {
|
||||
stringVector = wolv::util::splitString(states, ",", true);
|
||||
count = stringVector.size();
|
||||
}
|
||||
if (count == 1 && stringVector[0].empty())
|
||||
return;
|
||||
Indices closedFoldIncrements(count);
|
||||
i32 value = 0;
|
||||
for (i32 i = 0; i < count; ++i) {
|
||||
auto stateStr = stringVector[i];
|
||||
std::from_chars(stateStr.data(), stateStr.data() + stateStr.size(), value);
|
||||
closedFoldIncrements[i] = value;
|
||||
}
|
||||
resetCodeFoldStates();
|
||||
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 = 0.0f;
|
||||
|
||||
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];
|
||||
}
|
||||
FoldedLines 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_globalRowMaxChanged = true;
|
||||
}
|
||||
m_foldedLines[currentFoldedLine.m_row].loadSegments();
|
||||
}
|
||||
|
||||
void TextEditor::Lines::openCodeFold(const Range &key) {
|
||||
for (const 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) {
|
||||
FoldedLines 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;
|
||||
m_globalRowMaxChanged = 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 (count == 0)
|
||||
return;
|
||||
i32 id = getTokenId();
|
||||
|
||||
if (count > 0)
|
||||
m_curr += std::min(count,static_cast<i32>(m_tokens.size() - id));
|
||||
else
|
||||
m_curr += -std::min(-count,id);
|
||||
|
||||
}
|
||||
enum : u32 { Normal = 0, 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;
|
||||
}
|
||||
return isLocationValid(token.location);
|
||||
}
|
||||
|
||||
bool TextEditor::Lines::peek(const Token &token, const i32 index) {
|
||||
if (!isValid())
|
||||
return false;
|
||||
i32 id = getTokenId();
|
||||
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
@@ -5,6 +5,7 @@
|
||||
|
||||
namespace hex::ui {
|
||||
extern TextEditor::Palette s_paletteBase;
|
||||
using Keys = TextEditor::Keys;
|
||||
|
||||
template<class InputIt1, class InputIt2, class BinaryPredicate>
|
||||
bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) {
|
||||
@@ -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,25 +41,59 @@ namespace hex::ui {
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::colorize() {
|
||||
Keys TextEditor::Lines::getDeactivatedBlocks() {
|
||||
colorizeInternal();
|
||||
Keys 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;
|
||||
|
||||
std::smatch results;
|
||||
std::string id;
|
||||
if (m_languageDefinition.m_tokenize == nullptr) {
|
||||
m_languageDefinition.m_tokenize = [](strConstIter, strConstIter, strConstIter &, strConstIter &, PaletteIndex &) { return false; };
|
||||
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 +107,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 +200,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;
|
||||
@@ -177,7 +213,7 @@ namespace hex::ui {
|
||||
auto withinString = false;
|
||||
auto withinBlockComment = false;
|
||||
auto withinNotDef = false;
|
||||
auto currentLine = endLine;
|
||||
i32 currentLine;
|
||||
auto commentLength = 0;
|
||||
auto matchedBracket = false;
|
||||
|
||||
@@ -185,7 +221,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 +249,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 +263,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 +415,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 +437,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,10 @@
|
||||
|
||||
namespace hex::ui {
|
||||
|
||||
|
||||
using Coordinates = TextEditor::Coordinates;
|
||||
using Segments = TextEditor::Segments;
|
||||
|
||||
TextEditor::Line TextEditor::Line::trim(TrimMode trimMode) {
|
||||
if (m_chars.empty())
|
||||
return m_emptyLine;
|
||||
@@ -56,12 +60,11 @@ namespace hex::ui {
|
||||
}
|
||||
|
||||
i32 TextEditor::Line::stringTextSize(const std::string &str) const {
|
||||
i32 result = 0;
|
||||
if (str.empty())
|
||||
return 0;
|
||||
if (ImGui::GetFont() == nullptr) {
|
||||
fonts::CodeEditor().push();
|
||||
result = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, str.c_str(), nullptr, nullptr).x;
|
||||
i32 result = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, str.c_str(), nullptr, nullptr).x;
|
||||
fonts::CodeEditor().pop();
|
||||
return result;
|
||||
}
|
||||
@@ -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,31 @@ 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);
|
||||
|
||||
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 >= 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;
|
||||
i32 count = 0;
|
||||
u64 length;
|
||||
i32 increase;
|
||||
@@ -190,17 +209,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 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 +227,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 > 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;
|
||||
Segments 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) {
|
||||
Segments 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;
|
||||
|
||||
Segments 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);
|
||||
Segments 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 {coordinate.m_line + 1, 0};
|
||||
else
|
||||
return {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);
|
||||
Segments 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