mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 13:05:25 -05:00
Make the syntax highlighter more thread safety aware (#2585)
By creating copies of the required inputs on the main thread just before the task is spawned. A;so if task is still running when new data can be copied then the task is interrupted thus avoiding concurrency without mutexes. Atomics are used to signal state information used to determine what and when to spawn. Also includes update to pattern editor library and some fixes to syntax highlighting error when custom types defined inside namespaces were used inside the namespaces without the full qualified name and other small changes mostly to improve the current style.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -22,18 +22,13 @@
|
||||
#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>
|
||||
|
||||
#include <hex/providers/memory_provider.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include <popups/popup_question.hpp>
|
||||
#include <popups/popup_file_chooser.hpp>
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -43,11 +38,25 @@
|
||||
#include <wolv/utils/lock.hpp>
|
||||
|
||||
#include <fonts/fonts.hpp>
|
||||
#include <hex/api/content_registry/communication_interface.hpp>
|
||||
#include <hex/api/events/requests_gui.hpp>
|
||||
#include <hex/helpers/menu_items.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <romfs/romfs.hpp>
|
||||
#include <content/text_highlighting/pattern_language.hpp>
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
// Specialization for std::chrono::duration<double>
|
||||
template <>
|
||||
struct fmt::formatter<std::chrono::duration<double>> {
|
||||
constexpr auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.end();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::chrono::duration<double>& duration, FormatContext& ctx) const {
|
||||
return fmt::format_to(ctx.out(), "{} seconds", duration.count());
|
||||
}
|
||||
};
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
@@ -73,7 +82,7 @@ namespace hex::plugin::builtin {
|
||||
return;
|
||||
}
|
||||
|
||||
ui::TextEditor *editor = m_view->getTextEditor();
|
||||
ui::TextEditor const *editor = m_view->getTextEditor();
|
||||
if (editor != nullptr) {
|
||||
if (m_view->m_sourceCode.hasProviderSpecificSource(provider)) {
|
||||
this->close();
|
||||
@@ -306,16 +315,12 @@ namespace hex::plugin::builtin {
|
||||
m_editorRuntime = std::make_unique<pl::PatternLanguage>();
|
||||
ContentRegistry::PatternLanguage::configureRuntime(*m_editorRuntime, nullptr);
|
||||
|
||||
|
||||
this->registerEvents();
|
||||
this->registerMenuItems();
|
||||
this->registerHandlers();
|
||||
registerEvents();
|
||||
registerMenuItems();
|
||||
registerHandlers();
|
||||
|
||||
// Initialize the text editor with some basic help text
|
||||
m_textEditor.setOnCreateCallback([this](prv::Provider *provider, ui::TextEditor &editor) {
|
||||
if (m_sourceCode.isSynced() && !m_sourceCode.get(provider).empty())
|
||||
return;
|
||||
|
||||
m_textEditor.setOnCreateCallback([](auto, ui::TextEditor &editor) {
|
||||
std::string text = "hex.builtin.view.pattern_editor.default_help_text"_lang;
|
||||
text = "// " + wolv::util::replaceStrings(text, "\n", "\n// ");
|
||||
|
||||
@@ -999,9 +1004,9 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
m_frPopupIsClosed = false;
|
||||
} else if (!m_frPopupIsClosed) {
|
||||
m_frPopupIsClosed = true;
|
||||
m_findReplacePopupIsClosed = false;
|
||||
} else if (!m_findReplacePopupIsClosed) {
|
||||
m_findReplacePopupIsClosed = true;
|
||||
m_popupWindowHeight = 0;
|
||||
m_textEditor.get(provider).setTopMarginChanged(0);
|
||||
}
|
||||
@@ -1436,10 +1441,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (m_textEditor.get(provider).isTextChanged()) {
|
||||
m_textEditor.get(provider).setTextChanged(false);
|
||||
if (!m_hasUnevaluatedChanges.get(provider) ) {
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
m_changesWereParsed = false;
|
||||
}
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
m_lastEditorChangeTime = std::chrono::steady_clock::now();
|
||||
ImHexApi::Provider::markDirty();
|
||||
markPatternFileDirty(provider);
|
||||
@@ -1450,7 +1452,6 @@ namespace hex::plugin::builtin {
|
||||
|
||||
auto code = m_textEditor.get(provider).getText();
|
||||
EventPatternEditorChanged::post(code);
|
||||
|
||||
TaskManager::createBackgroundTask("hex.builtin.task.parsing_pattern", [this, code = std::move(code), provider](auto &){
|
||||
this->parsePattern(code, provider);
|
||||
|
||||
@@ -1464,14 +1465,35 @@ namespace hex::plugin::builtin {
|
||||
this->evaluatePattern(m_textEditor.get(provider).getText(), provider);
|
||||
}
|
||||
|
||||
if (m_textHighlighter.m_needsToUpdateColors && m_changesWereParsed && (m_runningParsers + m_runningEvaluators == 0)) {
|
||||
if (m_textHighlighter.getRunningColorizers() == 0) {
|
||||
m_textHighlighter.m_needsToUpdateColors = false;
|
||||
m_changesWereParsed = false;
|
||||
TaskManager::createBackgroundTask("HighlightSourceCode", [this](auto &) { m_textHighlighter.highlightSourceCode(); });
|
||||
} else {
|
||||
m_textHighlighter.interrupt();
|
||||
TaskHolder coloringTaskHolder;
|
||||
if (m_changesWereParsed || m_wasInterrupted || m_hasUnevaluatedChanges.get(provider)) {
|
||||
if (coloringTaskHolder.isRunning())
|
||||
interrupt();
|
||||
if (m_wasInterrupted)
|
||||
resetInterrupt();
|
||||
|
||||
m_changesWereParsed = false;
|
||||
if(!m_hasUnevaluatedChanges.get(provider)) {
|
||||
m_hasUncoloredChanges.get(provider) = true;
|
||||
m_changesWereColored = false;
|
||||
} else
|
||||
m_hasUncoloredChanges.get(provider) = false;
|
||||
}
|
||||
|
||||
|
||||
if (m_hasUncoloredChanges.get(provider) && (m_runningHighlighters + m_runningEvaluators == 0)) {
|
||||
|
||||
try {
|
||||
m_textHighlighter.get(provider).setViewPatternEditor(this);
|
||||
m_textHighlighter.get(provider).updateRequiredInputs();
|
||||
coloringTaskHolder = TaskManager::createBackgroundTask("HighlightSourceCode", [this,provider](auto &) { m_textHighlighter.get(provider).highlightSourceCode(); });
|
||||
m_hasUncoloredChanges.get(provider) = false;
|
||||
} catch (const std::out_of_range&) {
|
||||
interrupt();
|
||||
}
|
||||
} else if (m_changesWereColored) {
|
||||
m_textHighlighter.get(provider).setRequestedIdentifierColors();
|
||||
m_changesWereColored = false;
|
||||
}
|
||||
|
||||
if (m_dangerousFunctionCalled && !ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopup)) {
|
||||
@@ -1641,7 +1663,6 @@ namespace hex::plugin::builtin {
|
||||
m_changeTracker.get(provider) = wolv::io::ChangeTracker(file);
|
||||
m_changeTracker.get(provider).startTracking([this, provider]{ this->handleFileChange(provider); });
|
||||
}
|
||||
m_textHighlighter.m_needsToUpdateColors = false;
|
||||
TaskManager::createBackgroundTask("hex.builtin.task.parsing_pattern", [this, code, provider](auto&) { this->parsePattern(code, provider); });
|
||||
}
|
||||
}
|
||||
@@ -1651,7 +1672,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->getLongestLineLength());
|
||||
m_textEditor.get(provider).setLongestLineLength(m_editorRuntime->getInternals().preprocessor.get()->getLongestLineLength());
|
||||
|
||||
auto &patternVariables = m_patternVariables.get(provider);
|
||||
auto oldPatternVariables = std::move(patternVariables);
|
||||
@@ -1683,7 +1704,6 @@ namespace hex::plugin::builtin {
|
||||
patternVariables = std::move(oldPatternVariables);
|
||||
}
|
||||
|
||||
m_textHighlighter.m_needsToUpdateColors = true;
|
||||
m_changesWereParsed = true;
|
||||
m_runningParsers -= 1;
|
||||
}
|
||||
@@ -1707,7 +1727,6 @@ namespace hex::plugin::builtin {
|
||||
|
||||
m_accessHistory = {};
|
||||
m_accessHistoryIndex = 0;
|
||||
m_patternEvaluating = true;
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
|
||||
@@ -1835,13 +1854,20 @@ namespace hex::plugin::builtin {
|
||||
m_textEditor.get(provider).setText(wolv::util::preprocessText(code));
|
||||
m_sourceCode.get(provider) = code;
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
m_textHighlighter.m_needsToUpdateColors = false;
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.sync_pattern_source", [this](const ContentRegistry::Settings::SettingsValue &value) {
|
||||
m_sourceCode.enableSync(value.get<bool>(false));
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.suggest_patterns", [this](const ContentRegistry::Settings::SettingsValue &value) {
|
||||
m_suggestSupportedPatterns = value.get<bool>(true);
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.auto_apply_patterns", [this](const ContentRegistry::Settings::SettingsValue &value) {
|
||||
m_autoApplyPatterns = value.get<bool>(true);
|
||||
});
|
||||
|
||||
EventProviderOpened::subscribe(this, [this](prv::Provider *provider) {
|
||||
m_textEditor.get(provider).setLanguageDefinition(PatternLanguage());
|
||||
m_textEditor.get(provider).setShowWhitespaces(false);
|
||||
@@ -1893,7 +1919,6 @@ namespace hex::plugin::builtin {
|
||||
m_consoleEditor.get(newProvider).setScroll(m_consoleScroll.get(newProvider));
|
||||
|
||||
}
|
||||
m_textHighlighter.m_needsToUpdateColors = false;
|
||||
|
||||
});
|
||||
|
||||
@@ -2238,6 +2263,10 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
});
|
||||
|
||||
ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.pattern_parent_highlighting", [this](const ContentRegistry::Settings::SettingsValue &value) {
|
||||
m_parentHighlightingEnabled = bool(value.get<int>(false));
|
||||
});
|
||||
|
||||
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8 *data, size_t size, bool) -> std::optional<color_t> {
|
||||
std::ignore = data;
|
||||
std::ignore = size;
|
||||
@@ -2337,7 +2366,6 @@ namespace hex::plugin::builtin {
|
||||
m_textEditor.get(provider).setText(sourceCode);
|
||||
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
m_textHighlighter.m_needsToUpdateColors = false;
|
||||
return true;
|
||||
},
|
||||
.store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) {
|
||||
@@ -2553,81 +2581,6 @@ namespace hex::plugin::builtin {
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
ContentRegistry::MCP::registerTool(romfs::get("mcp/tools/execute_pattern_code.json").string(), [this](const nlohmann::json &data) -> nlohmann::json {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
auto sourceCode = data.at("source_code").get<std::string>();
|
||||
|
||||
this->evaluatePattern(sourceCode, provider);
|
||||
|
||||
// Wait until evaluation has finished
|
||||
while (m_runningEvaluators > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
auto lock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||
|
||||
auto evaluationResult = m_lastEvaluationResult.load();
|
||||
|
||||
nlohmann::json result = {
|
||||
{ "handle", provider->getID() },
|
||||
{ "result_code", evaluationResult }
|
||||
};
|
||||
return mcp::StructuredContent {
|
||||
.text = result.dump(),
|
||||
.data = result
|
||||
};
|
||||
});
|
||||
|
||||
ContentRegistry::MCP::registerTool(romfs::get("mcp/tools/get_pattern_console_content.json").string(), [this](const nlohmann::json &data) -> nlohmann::json {
|
||||
std::ignore = data;
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
// Wait until evaluation has finished
|
||||
while (m_runningEvaluators > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
auto lock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||
|
||||
auto consoleOutput = m_console.get(provider);
|
||||
|
||||
nlohmann::json result = {
|
||||
{ "handle", provider->getID() },
|
||||
{ "content", wolv::util::combineStrings(consoleOutput, "\n") }
|
||||
};
|
||||
return mcp::StructuredContent {
|
||||
.text = result.dump(),
|
||||
.data = result
|
||||
};
|
||||
});
|
||||
|
||||
ContentRegistry::MCP::registerTool(romfs::get("mcp/tools/get_patterns.json").string(), [this](const nlohmann::json &data) -> nlohmann::json {
|
||||
std::ignore = data;
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
// Wait until evaluation has finished
|
||||
while (m_runningEvaluators > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
auto lock = std::scoped_lock(ContentRegistry::PatternLanguage::getRuntimeLock());
|
||||
|
||||
pl::gen::fmt::FormatterJson formatter;
|
||||
auto formattedPatterns = formatter.format(ContentRegistry::PatternLanguage::getRuntime());
|
||||
|
||||
nlohmann::json result = {
|
||||
{ "handle", provider->getID() },
|
||||
{ "patterns", nlohmann::json::parse(std::string(formattedPatterns.begin(), formattedPatterns.end())) }
|
||||
};
|
||||
return mcp::StructuredContent {
|
||||
.text = result.dump(),
|
||||
.data = result
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
void ViewPatternEditor::handleFileChange(prv::Provider *provider) {
|
||||
@@ -2745,4 +2698,12 @@ namespace hex::plugin::builtin {
|
||||
ImGuiExt::TextFormattedWrapped("This will execute your code, output any log messages to the console window below and create a pattern tree that gets displayed in the Pattern Data view and highlights matching regions in the Hex Editor view.");
|
||||
}
|
||||
|
||||
ui::TextEditor *ViewPatternEditor::getTextEditor() {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (provider == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return &m_textEditor.get(provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user