1
0
mirror of synced 2024-12-23 21:14:51 +01:00
ImHex/plugins/builtin/source/content/command_palette_commands.cpp

352 lines
17 KiB
C++
Raw Normal View History

#include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization_manager.hpp>
2023-11-30 14:40:07 +01:00
#include <hex/api/shortcut_manager.hpp>
2021-08-29 22:15:18 +02:00
#include <hex/helpers/utils.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/providers/provider.hpp>
2021-08-29 22:15:18 +02:00
2023-12-01 14:07:10 +01:00
#include <wolv/math_eval/math_evaluator.hpp>
#include <wolv/utils/guards.hpp>
2023-07-09 12:53:31 +02:00
#include <wolv/utils/string.hpp>
namespace hex::plugin::builtin {
namespace {
class Value {
public:
enum class Unit {
Unitless,
Decimal,
Hexadecimal,
Binary,
Octal,
Bits,
Bytes,
Invalid
};
explicit Value(std::string value) {
if (!value.starts_with("0x") && !value.starts_with("0b")) {
auto index = value.find_first_not_of("0123456789.,");
if (index == std::string::npos) {
m_unit = Unit::Unitless;
} else {
std::tie(m_unit, m_multiplier) = parseUnit(value.substr(index));
value = value.substr(0, index);
}
} else {
m_unit = Unit::Unitless;
}
try {
if (!value.contains('.')) {
m_value = i128(std::stoull(value, nullptr, 0) * static_cast<long double>(m_multiplier));
} else {
m_value = std::stod(value) * m_multiplier;
}
2023-12-27 16:33:49 +01:00
} catch (const std::exception &) {
m_value = i128(0);
m_unit = Unit::Invalid;
m_unitString.clear();
m_multiplier = 1;
}
}
std::string formatAs(Value other) {
return std::visit([&, this]<typename T>(T value) -> std::string {
auto unit = other.getUnit();
auto multipler = other.getMultiplier();
bool isInteger = std::integral<T> && multipler == 1;
switch (m_unit) {
case Unit::Invalid:
if (this->getUnitString() != other.getUnitString())
return "hex.builtin.command.convert.invalid_conversion"_lang;
unit = Unit::Decimal;
[[fallthrough]];
case Unit::Unitless: {
switch (unit) {
case Unit::Unitless:
case Unit::Decimal:
if (isInteger)
return hex::format("{0}", value / multipler);
else
return hex::format("{0:.3f}", value / multipler);
case Unit::Hexadecimal:
return hex::format("0x{0:x}", u128(value / multipler));
case Unit::Binary:
return hex::format("0b{0:b}", u128(value / multipler));
case Unit::Octal:
return hex::format("0o{0:o}", u128(value / multipler));
case Unit::Bytes:
return hex::format("{0}", u128(value / multipler));
default:
return "hex.builtin.command.convert.invalid_conversion"_lang;
}
break;
}
case Unit::Bits: {
switch (unit) {
case Unit::Bits:
case Unit::Decimal:
if (isInteger)
return hex::format("{0}", value / multipler);
else
return hex::format("{0:.3f}", value / multipler);
case Unit::Hexadecimal:
return hex::format("0x{0:x}", u128(value / multipler));
case Unit::Binary:
return hex::format("0b{0:b}", u128(value / multipler));
case Unit::Octal:
return hex::format("0o{0:o}", u128(value / multipler));
case Unit::Bytes:
return hex::format("{0}", u128((value / multipler) / 8));
default:
return "hex.builtin.command.convert.invalid_conversion"_lang;
}
break;
}
case Unit::Bytes: {
switch (unit) {
case Unit::Bytes:
case Unit::Decimal:
if (isInteger)
return hex::format("{0}", value / multipler);
else
return hex::format("{0:.3f}", value / multipler);
case Unit::Hexadecimal:
return hex::format("0x{0:x}", u128(value / multipler));
case Unit::Binary:
return hex::format("0b{0:b}", u128(value / multipler));
case Unit::Octal:
return hex::format("0o{0:o}", u128(value / multipler));
case Unit::Bits:
return hex::format("{0}", u128((value / multipler) * 8));
default:
return "hex.builtin.command.convert.invalid_conversion"_lang;
}
break;
}
default:
return "hex.builtin.command.convert.invalid_input"_lang;
}
}, m_value);
}
[[nodiscard]] Unit getUnit() const { return m_unit; }
[[nodiscard]] double getMultiplier() const { return m_multiplier; }
[[nodiscard]] const std::string& getUnitString() const { return m_unitString; }
private:
std::pair<Unit, double> parseUnit(std::string unitString, bool parseMultiplier = true) {
auto unitStringCopy = unitString;
unitString = wolv::util::trim(unitString);
double multiplier = 1;
if (parseMultiplier && !unitString.starts_with("dec") && !unitString.starts_with("hex") && !unitString.starts_with("bin") && !unitString.starts_with("oct")) {
if (unitString.starts_with("Ki")) { multiplier = 1024ULL; unitString = unitString.substr(2); }
else if (unitString.starts_with("Mi")) { multiplier = 1024ULL * 1024ULL; unitString = unitString.substr(2); }
else if (unitString.starts_with("Gi")) { multiplier = 1024ULL * 1024ULL * 1024ULL; unitString = unitString.substr(2); }
else if (unitString.starts_with("Ti")) { multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL; unitString = unitString.substr(2); }
else if (unitString.starts_with("Pi")) { multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; unitString = unitString.substr(2); }
else if (unitString.starts_with("k")) { multiplier = 1E3; unitString = unitString.substr(1); }
else if (unitString.starts_with("M")) { multiplier = 1E6; unitString = unitString.substr(1); }
else if (unitString.starts_with("G")) { multiplier = 1E9; unitString = unitString.substr(1); }
else if (unitString.starts_with("T")) { multiplier = 1E12; unitString = unitString.substr(1); }
else if (unitString.starts_with("P")) { multiplier = 1E15; unitString = unitString.substr(1); }
else if (unitString.starts_with("E")) { multiplier = 1E18; unitString = unitString.substr(1); }
else if (unitString.starts_with("Z")) { multiplier = 1E21; unitString = unitString.substr(1); }
else if (unitString.starts_with("Y")) { multiplier = 1E24; unitString = unitString.substr(1); }
else if (unitString.starts_with("d")) { multiplier = 1E-1; unitString = unitString.substr(1); }
else if (unitString.starts_with("c")) { multiplier = 1E-2; unitString = unitString.substr(1); }
else if (unitString.starts_with("m")) { multiplier = 1E-3; unitString = unitString.substr(1); }
else if (unitString.starts_with("u")) { multiplier = 1E-6; unitString = unitString.substr(1); }
else if (unitString.starts_with("n")) { multiplier = 1E-9; unitString = unitString.substr(1); }
else if (unitString.starts_with("p")) { multiplier = 1E-12; unitString = unitString.substr(1); }
else if (unitString.starts_with("f")) { multiplier = 1E-15; unitString = unitString.substr(1); }
else if (unitString.starts_with("a")) { multiplier = 1E-18; unitString = unitString.substr(1); }
else if (unitString.starts_with("z")) { multiplier = 1E-21; unitString = unitString.substr(1); }
else if (unitString.starts_with("y")) { multiplier = 1E-24; unitString = unitString.substr(1); }
2023-12-27 16:33:49 +01:00
else { return parseUnit(unitString, false); }
}
unitString = wolv::util::trim(unitString);
2023-12-19 13:10:25 +01:00
m_unitString = unitString;
if (unitString.empty()) {
2023-12-27 16:33:49 +01:00
if (multiplier == 1) {
return { Unit::Unitless, 1 };
2023-12-27 16:33:49 +01:00
} else {
2023-12-19 13:10:25 +01:00
m_unitString = unitStringCopy;
return { Unit::Unitless, 1 };
}
2023-12-27 16:33:49 +01:00
} else if (unitString == "bit" || unitString == "bits" || unitString == "b") {
return { Unit::Bits, multiplier };
2023-12-27 16:33:49 +01:00
} else if (unitString == "byte" || unitString == "bytes" || unitString == "B") {
return { Unit::Bytes, multiplier };
2023-12-27 16:33:49 +01:00
} else if (unitString == "hex" || unitString == "hex.builtin.command.convert.hexadecimal"_lang.get()) {
return { Unit::Hexadecimal, multiplier };
2023-12-27 16:33:49 +01:00
} else if (unitString == "bin" || unitString == "hex.builtin.command.convert.binary"_lang.get()) {
return { Unit::Binary, multiplier };
2023-12-27 16:33:49 +01:00
} else if (unitString == "oct" || unitString == "hex.builtin.command.convert.octal"_lang.get()) {
return { Unit::Octal, multiplier };
2023-12-27 16:33:49 +01:00
} else if (unitString == "dec" || unitString == "hex.builtin.command.convert.decimal"_lang.get()) {
return { Unit::Decimal, multiplier };
2023-12-27 16:33:49 +01:00
} else {
return { Unit::Invalid, multiplier };
2023-12-27 16:33:49 +01:00
}
}
private:
Unit m_unit;
std::string m_unitString;
double m_multiplier = 1;
std::variant<i128, double> m_value;
};
std::vector<std::string> splitConversionCommandInput(const std::string &input) {
std::vector<std::string> parts = wolv::util::splitString(input, " ", true);
std::erase_if(parts, [](auto &part) { return part.empty(); });
return parts;
}
bool verifyConversionInput(const std::vector<std::string> &parts) {
if (parts.size() == 3) {
return parts[1] == "hex.builtin.command.convert.to"_lang.get() || parts[1] == "hex.builtin.command.convert.in"_lang.get() || parts[1] == "hex.builtin.command.convert.as"_lang.get();
} else {
return false;
}
}
std::string handleConversionCommand(const std::string &input) {
auto parts = splitConversionCommandInput(input);
if (!verifyConversionInput(parts))
return "hex.builtin.command.convert.invalid_input"_lang;
auto from = Value(parts[0]);
auto to = Value("1" + parts[2]);
return fmt::format("% {}", from.formatAs(to));
}
}
void registerCommandPaletteCommands() {
ContentRegistry::CommandPaletteCommands::add(
ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
"=",
"hex.builtin.command.calc.desc",
[](auto input) {
2023-12-01 14:07:10 +01:00
wolv::math_eval::MathEvaluator<long double> evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
2023-11-10 20:47:08 +01:00
std::optional<long double> result = evaluator.evaluate(input);
if (result.has_value())
return hex::format("{0} = {1}", input.data(), result.value());
ui/ux: Rewrite of the entire hex editor view to make it more flexible (#512) * ui/ux: Initial recreation of the hex editor view * ui/ux: Added back support for editing cells * ux: Make scrolling and selecting bytes feel nice again * ui/ux: Improved byte selecting, added footer * sys: Make math evaluator more generic to support integer only calculations * patterns: Moved value formatting into pattern language * ui/ux: Added Goto and Search popups, improved selection * ui: Added better tooltips for bookmarks and patterns * sys: Use worse hex search algorithm on macOS Sadly it still doesn't support `std::boyer_moore_horsepool_searcher` * ui: Added back missing events, menu items and shortcuts * fix: Bookmark highlighting being rendered off by one * fix: Various macOS build errors * fix: size_t is not u64 on macos * fix: std::fmod and std::pow not working with integer types on macos * fix: Missing semicolons * sys: Added proper integer pow function * ui: Added back support for custom encodings * fix: Editor not jumping to selection when selection gets changed * ui: Turn Hexii setting into a data visualizer * sys: Added back remaining shortcuts * sys: Remove old hex editor files * sys: Moved more legacy things away from the hex editor view, updated localization * fix: Hex editor scrolling behaving weirdly and inconsistently * sys: Cleaned up Hex editor code * sys: Added selection color setting, localized all new settings * fix: Search feature not working correctly * ui: Replace custom ImGui::Disabled function with native ImGui ones * ui: Fix bookmark tooltip rendering issues * fix: Another size_t not being 64 bit issue on MacOS
2022-05-27 20:42:07 +02:00
else if (evaluator.hasError())
return hex::format("Error: {}", *evaluator.getLastError());
else
return std::string("???");
});
ContentRegistry::CommandPaletteCommands::add(
ContentRegistry::CommandPaletteCommands::Type::KeywordCommand,
"/web",
"hex.builtin.command.web.desc",
[](auto input) {
return hex::format("hex.builtin.command.web.result"_lang, input.data());
},
[](auto input) {
hex::openWebpage(input);
});
ContentRegistry::CommandPaletteCommands::add(
ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
"$",
"hex.builtin.command.cmd.desc",
[](auto input) {
return hex::format("hex.builtin.command.cmd.result"_lang, input.data());
},
[](auto input) {
hex::executeCommand(input);
});
ContentRegistry::CommandPaletteCommands::addHandler(
ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
">",
[](const auto &input) {
std::vector<ContentRegistry::CommandPaletteCommands::impl::QueryResult> result;
for (const auto &[priority, entry] : ContentRegistry::Interface::impl::getMenuItems()) {
if (!entry.enabledCallback())
continue;
std::vector<std::string> names;
std::transform(entry.unlocalizedNames.begin(), entry.unlocalizedNames.end(), std::back_inserter(names), [](auto &name) { return Lang(name); });
if (auto combined = wolv::util::combineStrings(names, " -> "); hex::containsIgnoreCase(combined, input) && !combined.contains(ContentRegistry::Interface::impl::SeparatorValue) && !combined.contains(ContentRegistry::Interface::impl::SubMenuValue)) {
result.emplace_back(ContentRegistry::CommandPaletteCommands::impl::QueryResult {
std::move(combined),
[&entry](const auto&) { entry.callback(); }
});
}
}
return result;
},
[](auto input) {
return hex::format("Menu Item: {}", input.data());
});
ContentRegistry::CommandPaletteCommands::addHandler(
ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
".",
[](const auto &input) {
std::vector<ContentRegistry::CommandPaletteCommands::impl::QueryResult> result;
u32 index = 0;
for (const auto &provider : ImHexApi::Provider::getProviders()) {
ON_SCOPE_EXIT { index += 1; };
auto name = provider->getName();
if (!hex::containsIgnoreCase(name, input))
continue;
result.emplace_back(ContentRegistry::CommandPaletteCommands::impl::QueryResult {
provider->getName(),
[index](const auto&) { ImHexApi::Provider::setCurrentProvider(index); }
});
}
return result;
},
[](auto input) {
return hex::format("Provider: {}", input.data());
});
ContentRegistry::CommandPaletteCommands::add(
ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
"%",
"hex.builtin.command.convert.desc",
handleConversionCommand);
}
}