1
0
mirror of synced 2024-11-24 07:40:17 +01:00

impr: Various fixes and an enhancement for the pattern editor (#1528)

Fixed console error messages using doc comment syntax highlights. Fixed
results of find not updating when march case was toggled. Fixed syntax
highlights of nested ifdefs. Fixed editor cursor blinks if OS focus goes
to another window. Fixed Highlights of "\\\"" was incorrectly handled.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
paxcut 2024-03-21 05:58:20 -07:00 committed by GitHub
parent f5987fde5a
commit 3b3701135f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 126 additions and 93 deletions

View File

@ -175,7 +175,7 @@ public:
bool mCaseSensitive;
LanguageDefinition()
: mGlobalDocComment("/*!"), mDocComment("/**"), mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
{
}

View File

@ -604,7 +604,7 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
return mPalette[(int)PaletteIndex::Comment];
if (aGlyph.mMultiLineComment)
return mPalette[(int)PaletteIndex::MultiLineComment];
if (aGlyph.mDeactivated && !aGlyph.mPreprocessor)
if (aGlyph.mDeactivated)
return mPalette[(int)PaletteIndex::PreprocessorDeactivated];
auto const color = mPalette[(int)aGlyph.mColorIndex];
if (aGlyph.mPreprocessor) {
@ -619,10 +619,20 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
}
void TextEditor::HandleKeyboardInputs() {
ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift;
auto left = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow));
auto right = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow));
auto up = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow));
auto down = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow));
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
auto home = io.ConfigMacOSXBehaviors ? io.KeySuper && left : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
auto end = io.ConfigMacOSXBehaviors ? io.KeySuper && right : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
auto top = io.ConfigMacOSXBehaviors ? io.KeySuper && up : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
auto bottom = io.ConfigMacOSXBehaviors ? io.KeySuper && down : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
auto pageUp = io.ConfigMacOSXBehaviors ? ctrl && up : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp));
auto pageDown = io.ConfigMacOSXBehaviors ? ctrl && down : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown));
if (ImGui::IsWindowFocused()) {
if (ImGui::IsWindowHovered())
@ -638,25 +648,25 @@ void TextEditor::HandleKeyboardInputs() {
Undo();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
Redo();
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
else if (!ctrl && !alt && up)
MoveUp(1, shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
else if (!ctrl && !alt && down)
MoveDown(1, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
else if (!alt && left)
MoveLeft(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
else if (!alt && right)
MoveRight(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
else if (!alt && pageUp)
MoveUp(GetPageSize() - 4, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
else if (!alt && pageDown)
MoveDown(GetPageSize() - 4, shift);
else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
else if (!alt && top)
MoveTop(shift);
else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
else if (!alt && bottom)
MoveBottom(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
else if (!ctrl && !alt && home)
MoveHome(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
else if (!ctrl && !alt && end)
MoveEnd(shift);
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
Delete();
@ -722,7 +732,7 @@ void TextEditor::HandleKeyboardInputs() {
void TextEditor::HandleMouseInputs() {
ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
if (ImGui::IsWindowHovered()) {
@ -905,7 +915,10 @@ void TextEditor::Render() {
}
if (mState.mCursorPosition.mLine == lineNo && mShowCursor) {
auto focused = ImGui::IsWindowFocused();
bool focused = false;
ImGuiViewport *viewport = ImGui::GetWindowViewport();
if (viewport->PlatformUserData != NULL && ImGui::GetPlatformIO().Platform_GetWindowFocus(viewport))
focused = ImGui::IsWindowFocused();
// Highlight the current line (where the cursor is)
if (!HasSelection()) {
@ -2505,6 +2518,7 @@ void TextEditor::ColorizeInternal() {
auto firstChar = true; // there is no other non-whitespace characters in the line before
auto currentLine = 0;
auto currentIndex = 0;
auto commentLength = 0;
auto &startStr = mLanguageDefinition.mCommentStart;
auto &singleStartStr = mLanguageDefinition.mSingleLineComment;
auto &docStartStr = mLanguageDefinition.mDocComment;
@ -2516,6 +2530,14 @@ void TextEditor::ColorizeInternal() {
while (currentLine < endLine || currentIndex < endIndex) {
auto &line = mLines[currentLine];
auto setGlyphFlags = [&](int index) {
line[index].mMultiLineComment = withinComment;
line[index].mComment = withinSingleLineComment;
line[index].mDocComment = withinDocComment;
line[index].mGlobalDocComment = withinGlobalDocComment;
line[index].mDeactivated = withinNotDef;
};
if (currentIndex == 0) {
withinSingleLineComment = false;
withinPreproc = false;
@ -2532,24 +2554,12 @@ void TextEditor::ColorizeInternal() {
bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
if (withinString) {
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
if (c == '\"') {
if (currentIndex > 2 && line[currentIndex - 1].mChar == '\\' && line[currentIndex - 2].mChar != '\\') {
currentIndex += 1;
if (currentIndex < (int)line.size()) {
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
}
} else
withinString = false;
}
setGlyphFlags(currentIndex);
if (c == '\\') {
currentIndex++;
setGlyphFlags(currentIndex);
} else if (c == '\"')
withinString = false;
} else {
if (firstChar && c == mLanguageDefinition.mPreprocChar) {
withinPreproc = true;
@ -2594,7 +2604,6 @@ void TextEditor::ColorizeInternal() {
}
if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet);
} else
ifDefs.push_back(false);
@ -2608,7 +2617,6 @@ void TextEditor::ColorizeInternal() {
}
if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet);
} else
ifDefs.push_back(false);
@ -2616,20 +2624,17 @@ void TextEditor::ColorizeInternal() {
}
} else {
if (directive == "endif") {
if (ifDefs.size() > 1)
if (ifDefs.size() > 1) {
ifDefs.pop_back();
withinNotDef = !ifDefs.back();
withinNotDef = !ifDefs.back();
}
}
}
}
if (c == '\"') {
withinString = true;
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
setGlyphFlags(currentIndex);
} else {
auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; };
@ -2653,29 +2658,30 @@ void TextEditor::ColorizeInternal() {
if (isGlobalDocComment || isDocComment || isComment) {
commentStartLine = currentLine;
commentStartIndex = currentIndex;
if (isGlobalDocComment)
if (isGlobalDocComment) {
withinGlobalDocComment = true;
else if (isDocComment)
commentLength = 3;
} else if (isDocComment) {
withinDocComment = true;
else
commentLength = 3;
} else {
withinComment = true;
commentLength = 2;
}
}
}
inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
}
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDeactivated = withinNotDef;
setGlyphFlags(currentIndex);
auto &endStr = mLanguageDefinition.mCommentEnd;
if (compareBack(endStr, line)) {
withinComment = false;
withinDocComment = false;
withinGlobalDocComment = false;
commentStartLine = endLine;
commentStartIndex = endIndex;
if (compareBack(endStr, line) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) {
withinComment = false;
withinDocComment = false;
withinGlobalDocComment = false;
commentStartLine = endLine;
commentStartIndex = endIndex;
commentLength = 0;
}
}
}
@ -2684,6 +2690,7 @@ void TextEditor::ColorizeInternal() {
currentIndex += UTF8CharLength(c);
if (currentIndex >= (int)line.size()) {
withinNotDef = !ifDefs.back();
currentIndex = 0;
++currentLine;
}

View File

@ -14,6 +14,8 @@
#include <functional>
#include <TextEditor.h>
#include "popups/popup_file_chooser.hpp"
#include "hex/api/achievement_manager.hpp"
namespace pl::ptrn { class Pattern; }
@ -255,6 +257,39 @@ namespace hex::plugin::builtin {
void registerMenuItems();
void registerHandlers();
std::function<void()> importPatternFile = [&] {
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat")
paths.push_back(entry.path());
}
}
ui::PopupFileChooser::open(
basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
}
);
};
std::function<void()> exportPatternFile = [&] {
fs::openFileBrowser(
fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
}
);
};
void appendEditorText(const std::string &text);
void appendVariable(const std::string &type);
void appendArray(const std::string &type, size_t size);

View File

@ -135,6 +135,7 @@
"hex.builtin.menu.file.export.ips32": "IPS32 Patch",
"hex.builtin.menu.file.export.bookmark": "Bookmark",
"hex.builtin.menu.file.export.pattern": "Pattern File",
"hex.builtin.menu.file.export.pattern_file": "Export pattern File",
"hex.builtin.menu.file.export.data_processor": "Data Processor Workspace",
"hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!",
"hex.builtin.menu.file.export.report": "Report",
@ -147,6 +148,7 @@
"hex.builtin.menu.file.import.modified_file": "Modified File",
"hex.builtin.menu.file.import.bookmark": "Bookmark",
"hex.builtin.menu.file.import.pattern": "Pattern File",
"hex.builtin.menu.file.import.pattern_file": "Import pattern File",
"hex.builtin.menu.file.import.data_processor": "Data Processor Workspace",
"hex.builtin.menu.file.import.custom_encoding": "Custom Encoding File",
"hex.builtin.menu.file.import.modified_file.popup.invalid_size": "File selected do not have the same size as the current file. Both sizes must match",

View File

@ -3,13 +3,11 @@
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <pl/patterns/pattern.hpp>
#include <pl/core/preprocessor.hpp>
#include <pl/core/parser.hpp>
#include <pl/core/ast/ast_node_variable_decl.hpp>
#include <pl/core/ast/ast_node_type_decl.hpp>
#include <pl/core/ast/ast_node_builtin_type.hpp>
#include <hex/helpers/fs.hpp>
@ -22,11 +20,9 @@
#include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h>
#include <popups/popup_file_chooser.hpp>
#include <popups/popup_question.hpp>
#include <toasts/toast_notification.hpp>
#include <nlohmann/json.hpp>
#include <chrono>
#include <wolv/io/file.hpp>
@ -34,6 +30,8 @@
#include <wolv/utils/guards.hpp>
#include <wolv/utils/lock.hpp>
#include <content/global_actions.hpp>
namespace hex::plugin::builtin {
using namespace hex::literals;
@ -88,6 +86,9 @@ namespace hex::plugin::builtin {
langDef.mAutoIndentation = true;
langDef.mPreprocChar = '#';
langDef.mGlobalDocComment = "/*!";
langDef.mDocComment = "/**";
langDef.mName = "Pattern Language";
initialized = true;
@ -236,6 +237,14 @@ namespace hex::plugin::builtin {
bool clickedMenuFind = false;
bool clickedMenuReplace = false;
if (ImGui::BeginPopup("##pattern_editor_context_menu")) {
// no shortcut for this
if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false))
importPatternFile();
if (ImGui::MenuItem("hex.builtin.menu.file.export.pattern_file"_lang, nullptr, false))
exportPatternFile();
ImGui::Separator();
const bool hasSelection = m_textEditor.HasSelection();
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) {
m_textEditor.Cut();
@ -306,6 +315,12 @@ namespace hex::plugin::builtin {
m_replaceMode = true;
openFindPopup = true;
}
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt)
hex::plugin::builtin::saveProject();
if (ImGui::IsKeyPressed(ImGuiKey_O, false) && ImGui::GetIO().KeyAlt)
hex::plugin::builtin::openProject();
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt && ImGui::GetIO().KeyShift)
hex::plugin::builtin::saveProjectAs();
}
static std::string findWord;
@ -685,6 +700,8 @@ namespace hex::plugin::builtin {
if (altCPressed)
matchCase = !matchCase;
findReplaceHandler->SetMatchCase(&m_textEditor,matchCase);
position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true);
count = findReplaceHandler->GetMatches().size();
updateCount = true;
requestFocusFind = true;
}
@ -1733,39 +1750,11 @@ namespace hex::plugin::builtin {
void ViewPatternEditor::registerMenuItems() {
/* Import Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.pattern" }, ICON_VS_FILE_CODE, 4050, Shortcut::None,
[this] {
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat") {
paths.push_back(entry.path());
}
}
}
ui::PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
});
}, ImHexApi::Provider::isValid);
importPatternFile, ImHexApi::Provider::isValid);
/* Export Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None,
[this] {
fs::openFileBrowser(fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
});
}, [this] {
exportPatternFile, [this] {
return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid();
}
);