12#include <TargetConditionals.h>
17#include "absl/strings/ascii.h"
18#include "absl/strings/match.h"
19#include "absl/strings/str_cat.h"
34using util::FileDialogWrapper;
38static const char*
const kKeywords[] = {
39 "ADC",
"AND",
"ASL",
"BCC",
"BCS",
"BEQ",
"BIT",
"BMI",
"BNE",
"BPL",
40 "BRA",
"BRL",
"BVC",
"BVS",
"CLC",
"CLD",
"CLI",
"CLV",
"CMP",
"CPX",
41 "CPY",
"DEC",
"DEX",
"DEY",
"EOR",
"INC",
"INX",
"INY",
"JMP",
"JSR",
42 "JSL",
"LDA",
"LDX",
"LDY",
"LSR",
"MVN",
"NOP",
"ORA",
"PEA",
"PER",
43 "PHA",
"PHB",
"PHD",
"PHP",
"PHX",
"PHY",
"PLA",
"PLB",
"PLD",
"PLP",
44 "PLX",
"PLY",
"REP",
"ROL",
"ROR",
"RTI",
"RTL",
"RTS",
"SBC",
"SEC",
45 "SEI",
"SEP",
"STA",
"STP",
"STX",
"STY",
"STZ",
"TAX",
"TAY",
"TCD",
46 "TCS",
"TDC",
"TRB",
"TSB",
"TSC",
"TSX",
"TXA",
"TXS",
"TXY",
"TYA",
47 "TYX",
"WAI",
"WDM",
"XBA",
"XCE",
"ORG",
"LOROM",
"HIROM"};
49static const char*
const kIdentifiers[] = {
50 "abort",
"abs",
"acos",
"asin",
"atan",
"atexit",
51 "atof",
"atoi",
"atol",
"ceil",
"clock",
"cosh",
52 "ctime",
"div",
"exit",
"fabs",
"floor",
"fmod",
53 "getchar",
"getenv",
"isalnum",
"isalpha",
"isdigit",
"isgraph",
54 "ispunct",
"isspace",
"isupper",
"kbhit",
"log10",
"log2",
55 "log",
"memcmp",
"modf",
"pow",
"putchar",
"putenv",
56 "puts",
"rand",
"remove",
"rename",
"sinh",
"sqrt",
57 "srand",
"strcat",
"strcmp",
"strerror",
"time",
"tolower",
62 for (
auto& k : kKeywords)
65 for (
auto& k : kIdentifiers) {
68 language_65816.
mIdentifiers.insert(std::make_pair(std::string(k),
id));
72 std::make_pair<std::string, TextEditor::PaletteIndex>(
75 std::make_pair<std::string, TextEditor::PaletteIndex>(
78 std::make_pair<std::string, TextEditor::PaletteIndex>(
81 std::make_pair<std::string, TextEditor::PaletteIndex>(
82 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
85 std::make_pair<std::string, TextEditor::PaletteIndex>(
88 std::make_pair<std::string, TextEditor::PaletteIndex>(
91 std::make_pair<std::string, TextEditor::PaletteIndex>(
92 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?",
95 std::make_pair<std::string, TextEditor::PaletteIndex>(
98 std::make_pair<std::string, TextEditor::PaletteIndex>(
99 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
110 language_65816.
mName =
"65816";
112 return language_65816;
116 return absl::StrContains(name,
'.');
120 return !name.empty() && name[0] ==
'.';
124 const std::vector<std::string>& ignored_files) {
125 return std::ranges::find(ignored_files, name) != ignored_files.end();
129 static const std::array<const char*, 11> kSkippedDirectories = {
130 ".git",
".context",
".idea",
".vscode",
131 "build",
"build_ai",
"build_agent",
"build_test",
132 "node_modules",
"dist",
"out"};
133 for (
const char* skipped : kSkippedDirectories) {
134 if (name == skipped) {
145 std::sort(item->
files.begin(), item->
files.end());
148 return lhs.name < rhs.name;
156 std::vector<std::string> ignored_files;
157#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
158 std::ifstream gitignore(folder +
"/.gitignore");
159 if (gitignore.good()) {
161 while (std::getline(gitignore, line)) {
162 if (line.empty() || line[0] ==
'#' || line[0] ==
'!') {
165 ignored_files.push_back(line);
172 std::error_code path_ec;
173 std::filesystem::path root_path =
174 std::filesystem::weakly_canonical(folder, path_ec);
177 root_path = std::filesystem::absolute(folder, path_ec);
182 current_folder.
name = root_path.string();
184 std::error_code root_ec;
185 for (
const auto& entry :
186 std::filesystem::directory_iterator(root_path, root_ec)) {
191 const std::string entry_name = entry.path().filename().string();
196 std::error_code type_ec;
197 if (entry.is_regular_file(type_ec)) {
202 current_folder.
files.push_back(entry_name);
211 folder_item.
name = entry_name;
213 std::error_code sub_ec;
214 for (
const auto& sub_entry :
215 std::filesystem::directory_iterator(entry.path(), sub_ec)) {
220 const std::string sub_name = sub_entry.path().filename().string();
225 std::error_code sub_type_ec;
226 if (sub_entry.is_regular_file(sub_type_ec)) {
231 folder_item.
files.push_back(sub_name);
235 if (!sub_entry.is_directory(sub_type_ec) ||
241 subfolder_item.
name = sub_name;
242 std::error_code leaf_ec;
243 for (
const auto& leaf_entry :
244 std::filesystem::directory_iterator(sub_entry.path(), leaf_ec)) {
248 const std::string leaf_name = leaf_entry.path().filename().string();
252 std::error_code leaf_type_ec;
253 if (!leaf_entry.is_regular_file(leaf_type_ec)) {
260 subfolder_item.
files.push_back(leaf_name);
262 folder_item.
subfolders.push_back(std::move(subfolder_item));
265 current_folder.
subfolders.push_back(std::move(folder_item));
269 return current_folder;
273 const std::filesystem::path& path,
const std::string& label) {
274 std::ifstream file(path);
275 if (!file.is_open()) {
281 while (std::getline(file, line)) {
282 const size_t start = line.find_first_not_of(
" \t");
283 if (start == std::string::npos) {
288 if (line.compare(start, label.size(), label) != 0) {
293 size_t pos = start + label.size();
294 while (pos < line.size() && (line[pos] ==
' ' || line[pos] ==
'\t')) {
298 if (pos < line.size() && line[pos] ==
':') {
300 loc.
file = path.string();
301 loc.
line = line_index;
302 loc.
column =
static_cast<int>(start);
313 const auto ext = path.extension().string();
314 return ext ==
".asm" || ext ==
".inc" || ext ==
".s";
319 int line_one_based = 0;
320 int column_one_based = 1;
333 if (!std::isdigit(
static_cast<unsigned char>(c))) {
338 const int v = std::stoi(s);
339 return v > 0 ? std::optional<int>(v) : std::nullopt;
346 if (file_ref.empty()) {
349 const std::filesystem::path p(file_ref);
354 const std::string& reference) {
355 const std::string trimmed =
356 std::string(absl::StripAsciiWhitespace(reference));
357 if (trimmed.empty()) {
362 if (
const size_t pos = trimmed.find(
"#L"); pos != std::string::npos) {
363 const std::string file =
364 std::string(absl::StripAsciiWhitespace(trimmed.substr(0, pos)));
365 const std::string line_str =
366 std::string(absl::StripAsciiWhitespace(trimmed.substr(pos + 2)));
379 const size_t last_colon = trimmed.rfind(
':');
380 if (last_colon == std::string::npos) {
384 const std::string tail =
385 std::string(absl::StripAsciiWhitespace(trimmed.substr(last_colon + 1)));
390 const size_t second_last_colon = (last_colon == 0)
392 : trimmed.rfind(
':', last_colon - 1);
394 if (second_last_colon != std::string::npos) {
395 const std::string file = std::string(
396 absl::StripAsciiWhitespace(trimmed.substr(0, second_last_colon)));
397 const std::string line_str =
398 std::string(absl::StripAsciiWhitespace(trimmed.substr(
399 second_last_colon + 1, last_colon - second_last_colon - 1)));
400 const std::string col_str = tail;
406 if (!line.has_value() || !col.has_value()) {
412 const std::string file =
413 std::string(absl::StripAsciiWhitespace(trimmed.substr(0, last_colon)));
424 const std::string& reference) {
425 const std::string trimmed =
426 std::string(absl::StripAsciiWhitespace(reference));
427 if (trimmed.empty()) {
432 if (
const size_t pos = trimmed.rfind(
'#'); pos != std::string::npos) {
433 const std::string file =
434 std::string(absl::StripAsciiWhitespace(trimmed.substr(0, pos)));
435 const std::string sym =
436 std::string(absl::StripAsciiWhitespace(trimmed.substr(pos + 1)));
444 const size_t last_colon = trimmed.rfind(
':');
445 if (last_colon == std::string::npos) {
449 const std::string file =
450 std::string(absl::StripAsciiWhitespace(trimmed.substr(0, last_colon)));
451 const std::string sym =
452 std::string(absl::StripAsciiWhitespace(trimmed.substr(last_colon + 1)));
467 const std::filesystem::path& root,
const std::string& file_ref) {
468 std::filesystem::path p(file_ref);
469 if (p.is_absolute()) {
471 if (std::filesystem::exists(p, ec) &&
472 std::filesystem::is_regular_file(p, ec)) {
480 const std::filesystem::path candidate = root / p;
482 if (std::filesystem::exists(candidate, ec) &&
483 std::filesystem::is_regular_file(candidate, ec)) {
489 const std::string want_suffix = p.generic_string();
490 const std::string want_name = p.filename().string();
493 if (!std::filesystem::exists(root, ec)) {
497 std::filesystem::recursive_directory_iterator it(
498 root, std::filesystem::directory_options::skip_permission_denied, ec);
499 const std::filesystem::recursive_directory_iterator end;
500 for (; it != end && !ec; it.increment(ec)) {
501 const auto& entry = *it;
502 if (entry.is_directory()) {
503 const auto name = entry.path().filename().string();
504 if (!name.empty() && name.front() ==
'.') {
505 it.disable_recursion_pending();
506 }
else if (name ==
"build" || name ==
"build_ai" || name ==
"build-ios" ||
507 name ==
"build-ios-sim" || name ==
"build-wasm" ||
508 name ==
"node_modules") {
509 it.disable_recursion_pending();
514 if (!entry.is_regular_file()) {
521 const std::string cand = entry.path().generic_string();
522 if (!want_suffix.empty() && absl::EndsWith(cand, want_suffix)) {
525 if (!want_name.empty() && entry.path().filename() == want_name) {
534 const std::filesystem::path& root,
const std::string& label) {
536 if (!std::filesystem::exists(root, ec)) {
540 std::filesystem::recursive_directory_iterator it(
541 root, std::filesystem::directory_options::skip_permission_denied, ec);
542 const std::filesystem::recursive_directory_iterator end;
543 for (; it != end && !ec; it.increment(ec)) {
544 const auto& entry = *it;
545 if (entry.is_directory()) {
546 const auto name = entry.path().filename().string();
547 if (!name.empty() && name.front() ==
'.') {
548 it.disable_recursion_pending();
549 }
else if (name ==
"build" || name ==
"build_ai" || name ==
"build-ios" ||
550 name ==
"build-ios-sim" || name ==
"build-wasm" ||
551 name ==
"node_modules") {
552 it.disable_recursion_pending();
557 if (!entry.is_regular_file()) {
565 if (
auto loc =
FindLabelInFile(entry.path(), label); loc.has_value()) {
588 panel_manager->RegisterEditorPanel(std::make_unique<AssemblyFileBrowserPanel>(
592 panel_manager->RegisterEditorPanel(std::make_unique<AssemblySymbolsPanel>(
596 panel_manager->RegisterEditorPanel(std::make_unique<AssemblyBuildOutputPanel>(
600 panel_manager->RegisterEditorPanel(std::make_unique<AssemblyToolbarPanel>(
606 return absl::OkStatus();
610 if (symbol.empty()) {
611 return absl::InvalidArgumentError(
"Symbol is empty");
614 std::filesystem::path root;
621 return absl::FailedPreconditionError(
622 "No code folder loaded (open a folder or set project code_folder)");
625 const std::string root_string = root.string();
638 const auto& cached = it->second;
641 return absl::InternalError(
"Failed to open file for symbol: " + symbol);
646 return absl::InternalError(
"No active text editor");
649 editor->SetCursorPosition(
651 editor->SelectWordUnderCursor();
652 return absl::OkStatus();
656 return absl::NotFoundError(
"Symbol not found: " + symbol);
659 const auto loc = FindLabelInFolder(root, symbol);
660 if (!loc.has_value()) {
662 return absl::NotFoundError(
"Symbol not found: " + symbol);
670 return absl::InternalError(
"Failed to open file for symbol: " + symbol);
675 return absl::InternalError(
"No active text editor");
679 editor->SelectWordUnderCursor();
680 return absl::OkStatus();
684 if (reference.empty()) {
685 return absl::InvalidArgumentError(
"Reference is empty");
688 if (
auto file_ref = ParseAsmFileLineRef(reference); file_ref.has_value()) {
689 std::filesystem::path root;
696 return absl::FailedPreconditionError(
697 "No code folder loaded (open a folder or set project code_folder)");
704 auto path_or = FindAsmFileInFolder(root, file_ref->file_ref);
705 if (!path_or.has_value()) {
706 return absl::NotFoundError(
"File not found: " + file_ref->file_ref);
711 return absl::InternalError(
"Failed to open file: " + path_or->string());
714 const int line0 = std::max(0, file_ref->line_one_based - 1);
715 const int col0 = std::max(0, file_ref->column_one_based - 1);
718 return absl::InternalError(
"No active text editor");
721 editor->SelectWordUnderCursor();
722 return absl::OkStatus();
725 if (
auto file_ref = ParseAsmFileSymbolRef(reference); file_ref.has_value()) {
726 std::filesystem::path root;
733 return absl::FailedPreconditionError(
734 "No code folder loaded (open a folder or set project code_folder)");
741 auto path_or = FindAsmFileInFolder(root, file_ref->file_ref);
742 if (!path_or.has_value()) {
743 return absl::NotFoundError(
"File not found: " + file_ref->file_ref);
746 auto loc = FindLabelInFile(*path_or, file_ref->symbol);
747 if (!loc.has_value()) {
748 return absl::NotFoundError(absl::StrCat(
749 "Symbol not found in ", file_ref->file_ref,
": ", file_ref->symbol));
754 return absl::InternalError(
"Failed to open file: " + loc->file);
759 return absl::InternalError(
"No active text editor");
762 editor->SelectWordUnderCursor();
763 return absl::OkStatus();
818 if (ImGui::BeginMenuBar()) {
827 const char* file_label =
829 ImGui::Text(
"%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
836 editor->
Render(
"##asm_editor",
837 ImVec2(0, -ImGui::GetFrameHeightWithSpacing()));
854 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
858 ImGui::TextDisabled(
"No folder opened");
863 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
"%s",
873 ImGui::TextDisabled(
"No symbols loaded.");
876 "Apply a patch or load external symbols to populate this list.");
881 static char filter[256] =
"";
882 ImGui::SetNextItemWidth(-1);
883 ImGui::InputTextWithHint(
"##symbol_filter",
889 if (ImGui::BeginChild(
"##symbol_list", ImVec2(0, 0),
false)) {
890 for (
const auto& [name, symbol] :
symbols_) {
892 if (filter[0] !=
'\0' && name.find(filter) == std::string::npos) {
896 ImGui::PushID(name.c_str());
897 if (ImGui::Selectable(name.c_str())) {
900 ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60);
901 ImGui::TextDisabled(
"$%06X", symbol.address);
910 ImGui::Text(
"Errors: %zu Warnings: %zu",
last_errors_.size(),
919 if (has_active_file) {
928 bool apply_disabled = !has_rom || !has_active_file;
929 ImGui::BeginDisabled(apply_disabled);
930 if (ImGui::Button(
ICON_MD_BUILD " Apply to ROM", ImVec2(140, 0))) {
936 ImGui::EndDisabled();
937 if (apply_disabled &&
938 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
940 ImGui::SetTooltip(
"Load a ROM first");
942 ImGui::SetTooltip(
"Open an assembly file first");
948 if (ImGui::BeginChild(
"##build_log", ImVec2(0, 0),
true)) {
952 ImVec4(1.0f, 0.4f, 0.4f, 1.0f));
958 ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
962 ImGui::TextDisabled(
"No build output");
969 float button_size = 32.0f;
973 if (!folder.empty()) {
977 if (ImGui::IsItemHovered())
978 ImGui::SetTooltip(
"Open Folder");
983 if (!filename.empty()) {
987 if (ImGui::IsItemHovered())
988 ImGui::SetTooltip(
"Open File");
992 ImGui::BeginDisabled(!can_save);
993 if (ImGui::Button(
ICON_MD_SAVE, ImVec2(button_size, button_size))) {
996 ImGui::EndDisabled();
997 if (ImGui::IsItemHovered())
998 ImGui::SetTooltip(
"Save File");
1005 ImGui::BeginDisabled(!can_save);
1009 ImGui::EndDisabled();
1010 if (ImGui::IsItemHovered())
1011 ImGui::SetTooltip(
"Validate (Ctrl+B)");
1015 ImGui::BeginDisabled(!can_apply);
1016 if (ImGui::Button(
ICON_MD_BUILD, ImVec2(button_size, button_size))) {
1019 ImGui::EndDisabled();
1020 if (ImGui::IsItemHovered())
1021 ImGui::SetTooltip(
"Apply to ROM (Ctrl+Shift+B)");
1030 ImGuiTabBarFlags_Reorderable |
1031 ImGuiTabBarFlags_AutoSelectNewTabs |
1032 ImGuiTabBarFlags_FittingPolicyScroll)) {
1035 if (file_id >=
files_.size()) {
1040 std::string filename =
files_[file_id];
1041 size_t pos = filename.find_last_of(
"/\\");
1042 if (pos != std::string::npos) {
1043 filename = filename.substr(pos + 1);
1047 ImGuiTabItemFlags flags = is_active ? ImGuiTabItemFlags_SetSelected : 0;
1048 bool tab_open =
true;
1050 if (ImGui::BeginTabItem(filename.c_str(), &tab_open, flags)) {
1056 ImGui::EndTabItem();
1084 return absl::OkStatus();
1089 ImGui::Begin(
"Assembly Editor", &
active_, ImGuiWindowFlags_MenuBar);
1096 return absl::OkStatus();
1102 const char* file_label =
1104 ImGui::Text(
"%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
1107 editor->
CanUndo() ?
"*" :
" ",
1110 editor->
Render(
"##asm_editor", ImVec2(0, 0));
1123 return absl::FailedPreconditionError(
"No active file to save.");
1127 std::ofstream file(path);
1128 if (!file.is_open()) {
1129 return absl::InvalidArgumentError(
1130 absl::StrCat(
"Cannot write file: ", path));
1135 return absl::OkStatus();
1160 if (ImGui::BeginChild(
"##current_folder", ImVec2(0, 0),
true,
1161 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1162 if (ImGui::BeginTable(
"##file_table", 2,
1163 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
1164 ImGuiTableFlags_Resizable |
1165 ImGuiTableFlags_Sortable)) {
1166 ImGui::TableSetupColumn(
"Name", ImGuiTableColumnFlags_WidthFixed, 256.0f);
1167 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthStretch);
1169 ImGui::TableHeadersRow();
1172 ImGui::TableNextRow();
1173 ImGui::TableNextColumn();
1174 if (ImGui::Selectable(file.c_str())) {
1177 ImGui::TableNextColumn();
1178 ImGui::Text(
"File");
1182 ImGui::TableNextRow();
1183 ImGui::TableNextColumn();
1184 if (ImGui::TreeNode(subfolder.name.c_str())) {
1185 for (
const auto& file : subfolder.files) {
1186 ImGui::TableNextRow();
1187 ImGui::TableNextColumn();
1188 if (ImGui::Selectable(file.c_str())) {
1190 subfolder.name,
"/", file));
1192 ImGui::TableNextColumn();
1193 ImGui::Text(
"File");
1197 ImGui::TableNextColumn();
1198 ImGui::Text(
"Folder");
1209 if (ImGui::BeginMenu(
"File")) {
1212 if (!filename.empty()) {
1224 if (ImGui::BeginMenu(
"Edit")) {
1250 if (filename.empty()) {
1257 if (
files_[file_id] == filename) {
1268 int new_file_id =
files_.size();
1269 files_.push_back(std::string(filename));
1280 open_files_[new_file_id].SetShowWhitespaces(
false);
1283 }
catch (
const std::exception& ex) {
1284 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error opening file: %s\n",
1291 return absl::OkStatus();
1296 return absl::OkStatus();
1301 return absl::OkStatus();
1306 return absl::OkStatus();
1311 return absl::OkStatus();
1320 return absl::FailedPreconditionError(
"No file is currently active");
1348 size_t line_pos = error.find(
':');
1349 if (line_pos != std::string::npos) {
1350 size_t num_start = line_pos + 1;
1351 size_t num_end = error.find(
':', num_start);
1352 if (num_end != std::string::npos) {
1353 std::string line_str = error.substr(num_start, num_end - num_start);
1355 int line = std::stoi(line_str);
1356 markers[line] = error;
1369 return absl::OkStatus();
1374 return absl::FailedPreconditionError(
"No ROM is loaded");
1378 return absl::FailedPreconditionError(
"No file is currently active");
1394 std::vector<uint8_t> rom_data =
rom_->
vector();
1401 return result.status();
1404 if (result->success) {
1416 return absl::OkStatus();
1419 return absl::InternalError(
"Patch application failed");
1435 for (
const auto& error : result.
errors) {
1438 size_t first_colon = error.find(
':');
1439 if (first_colon != std::string::npos) {
1440 size_t second_colon = error.find(
':', first_colon + 1);
1441 if (second_colon != std::string::npos) {
1442 std::string line_str =
1443 error.substr(first_colon + 1, second_colon - (first_colon + 1));
1444 int line = std::stoi(line_str);
1448 markers[line] = error;
1471 if (ImGui::BeginMenu(
"Assemble")) {
1483 if (ImGui::MenuItem(
ICON_MD_BUILD " Apply to ROM",
"Ctrl+Shift+B",
false,
1484 has_active_file && has_rom)) {
1495 if (!sym_file.empty()) {
1496 std::string abs_path =
1504 "Successfully loaded external symbols from " + sym_file,
1510 "Failed to load symbols: " + std::string(status.message()),
1517 "Project does not specify a symbols file.",
1526 if (ImGui::MenuItem(
ICON_MD_LIST " Show Symbols",
nullptr,
1534 ImGui::TextDisabled(
"Errors: %zu, Warnings: %zu",
last_errors_.size(),
1540 if (ImGui::BeginMenu(
"Version")) {
1543 has_version_manager)) {
1544 if (has_version_manager) {
1545 ImGui::OpenPopup(
"Create Snapshot");
1550 if (ImGui::BeginPopupModal(
"Create Snapshot",
nullptr,
1551 ImGuiWindowFlags_AlwaysAutoResize)) {
1552 static char message[256] =
"";
1553 ImGui::InputText(
"Message", message,
sizeof(message));
1555 if (ImGui::Button(
"Create", ImVec2(120, 0))) {
1557 if (result.ok() && result->success) {
1560 "Snapshot Created: " + result->commit_hash,
1565 std::string err = result.ok()
1567 : std::string(result.status().message());
1572 ImGui::CloseCurrentPopup();
1576 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
1577 ImGui::CloseCurrentPopup();
1591 ImGui::SetNextWindowSize(ImVec2(350, 400), ImGuiCond_FirstUseEver);
1594 ImGui::TextDisabled(
"No symbols loaded.");
1595 ImGui::TextDisabled(
"Apply a patch to load symbols.");
1598 static char filter[256] =
"";
1599 ImGui::InputTextWithHint(
"##symbol_filter",
"Filter symbols...", filter,
1604 if (ImGui::BeginChild(
"##symbol_list", ImVec2(0, 0),
true)) {
1605 for (
const auto& [name, symbol] :
symbols_) {
1607 if (filter[0] !=
'\0' && name.find(filter) == std::string::npos) {
1611 ImGui::PushID(name.c_str());
1612 if (ImGui::Selectable(name.c_str())) {
1616 ImGui::SameLine(200);
1617 ImGui::TextDisabled(
"$%06X", symbol.address);
static const Palette & GetDarkPalette()
Coordinates GetCursorPosition() const
std::string GetText() const
int GetTotalLines() const
void Render(const char *aTitle, const ImVec2 &aSize=ImVec2(), bool aBorder=false)
std::map< int, std::string > ErrorMarkers
void SetErrorMarkers(const ErrorMarkers &aMarkers)
const LanguageDefinition & GetLanguageDefinition() const
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
const auto & vector() const
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
absl::StatusOr< AsarPatchResult > ApplyPatch(const std::string &patch_path, std::vector< uint8_t > &rom_data, const std::vector< std::string > &include_paths={})
absl::Status ValidateAssembly(const std::string &asm_path)
absl::Status Initialize()
absl::Status LoadSymbolsFromFile(const std::string &symbol_path)
std::map< std::string, AsarSymbol > GetSymbolTable() const
absl::StatusOr< SnapshotResult > CreateSnapshot(const std::string &message)
std::string current_file_
TextEditor::Coordinates active_cursor_position() const
absl::Status ValidateCurrentFile()
std::vector< std::string > last_warnings_
std::map< std::string, core::AsarSymbol > symbols_
absl::Status Save() override
std::vector< TextEditor > open_files_
bool HasActiveFile() const
absl::Status Load() override
void Initialize() override
void ClearSymbolJumpCache()
void ChangeActiveFile(const std::string_view &filename)
absl::Status JumpToReference(const std::string &reference)
TextEditor * GetActiveEditor()
absl::Status Copy() override
absl::flat_hash_map< std::string, AsmSymbolLocation > symbol_jump_cache_
void UpdateErrorMarkers(const core::AsarPatchResult &result)
void OpenFolder(const std::string &folder_path)
absl::flat_hash_set< std::string > symbol_jump_negative_cache_
std::string active_file_path() const
absl::Status Paste() override
FolderItem current_folder_
absl::Status Update() override
std::vector< std::string > files_
void DrawToolbarContent()
absl::Status Redo() override
ImVector< int > active_files_
absl::Status Undo() override
void DrawSymbolsContent()
absl::Status ApplyPatchToRom()
absl::Status Cut() override
std::vector< std::string > last_errors_
std::string symbol_jump_root_
absl::Status JumpToSymbolDefinition(const std::string &symbol)
EditorDependencies dependencies_
void RegisterEditorPanel(std::unique_ptr< EditorPanel > panel)
Register an EditorPanel instance for central drawing.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
RAII guard for ImGui style colors.
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static std::string ShowOpenFolderDialog()
ShowOpenFolderDialog opens a file dialog and returns the selected folder path. Uses global feature fl...
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_CONTENT_CUT
#define ICON_MD_FILE_OPEN
#define ICON_MD_CAMERA_ALT
#define ICON_MD_FILE_UPLOAD
#define ICON_MD_CONTENT_PASTE
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_CONTENT_COPY
std::optional< int > ParsePositiveInt(const std::string &s)
bool LooksLikeAssemblyPathRef(const std::string &file_ref)
std::optional< AsmFileSymbolRef > ParseAsmFileSymbolRef(const std::string &reference)
std::optional< AsmSymbolLocation > FindLabelInFolder(const std::filesystem::path &root, const std::string &label)
std::optional< AsmSymbolLocation > FindLabelInFile(const std::filesystem::path &path, const std::string &label)
bool ShouldSkipDirectory(const std::string &name)
std::optional< AsmFileLineRef > ParseAsmFileLineRef(const std::string &reference)
bool IsIgnoredFile(const std::string &name, const std::vector< std::string > &ignored_files)
FolderItem LoadFolder(const std::string &folder)
std::optional< std::filesystem::path > FindAsmFileInFolder(const std::filesystem::path &root, const std::string &file_ref)
void SortFolderItem(FolderItem *item)
bool HasFileExtension(const std::string &name)
bool IsAssemblyLikeFile(const std::filesystem::path &path)
bool IsHiddenName(const std::string &name)
Editors are the view controllers for the application.
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
TextEditor::LanguageDefinition GetAssemblyLanguageDef()
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
std::string LoadFile(const std::string &filename)
Loads the entire contents of a file into a string.
static Coordinates Invalid()
TokenRegexStrings mTokenRegexStrings
std::string mSingleLineComment
std::string mCommentStart
Asar patch result information.
std::vector< std::string > errors
std::vector< std::string > warnings
project::YazeProject * project
ToastManager * toast_manager
core::VersionManager * version_manager
PanelManager * panel_manager
std::vector< FolderItem > subfolders
std::vector< std::string > files
std::string GetAbsolutePath(const std::string &relative_path) const
std::string symbols_filename