38 namespace fs = std::filesystem;
43 .name =
"Cthulhu GUI",
46 .desc =
"Cthulhu Compiler Collection GUI",
47 .author =
"Elliot Haisley",
57 static std::vector<TraceArenaWidget> gTraceWidgets = {
63 static void install_trace_arenas()
68 ImGui::SetAllocatorFunctions(
69 [](
size_t size,
void *user) {
73 [](
void *ptr,
void *user) {
83 std::vector<ed::LanguageInfoPanel> languages;
84 std::vector<ed::PluginInfoPanel> plugins;
85 std::vector<ed::TargetInfoPanel> targets;
87 void draw_content()
override
89 if (ImGui::BeginTabBar(
"ModuleTabs"))
91 if (ImGui::BeginTabItem(
"Languages"))
93 for (
auto &lang : languages)
101 if (ImGui::BeginTabItem(
"Plugins"))
103 for (
auto &plugin : plugins)
111 if (ImGui::BeginTabItem(
"Targets"))
113 for (
auto &target : targets)
134 if (mod.
type & eModLanguage)
136 languages.emplace_back(*mod.
lang);
139 if (mod.
type & eModPlugin)
141 plugins.emplace_back(*mod.
plugin);
144 if (mod.
type & eModTarget)
146 targets.emplace_back(*mod.
target);
157 for (
size_t i = 0; i < len; i++)
169 bool result = IEditorPanel::menu_item(shortcut);
171 ImGui::SetItemTooltip(
"Load a module to enable this panel");
178 return languages.empty() && plugins.empty() && targets.empty();
196 if (loader.is_empty())
198 if (ImGui::Button(
"Load default modules"))
200 loader.load_default_modules(support);
207 ImGui::Text(
"Default modules loaded");
213 if (!loader.is_empty())
216 return IEditorPanel::menu_item(shortcut);
226 int mask = eModLanguage;
228 ImGui::FileBrowser file_browser { ImGuiFileBrowserFlags_MultipleSelection | ImGuiFileBrowserFlags_ConfirmOnEnter };
231 std::string os_error;
239 file_browser.SetTitle(
"Open Shared Module");
240 file_browser.SetTypeFilters({
".dll",
".so",
".dylib" });
245 ImGui::CheckboxFlags(
"Language", &mask, eModLanguage);
246 ImGui::SameLine(); ImGui::CheckboxFlags(
"Plugin", &mask, eModPlugin);
247 ImGui::SameLine(); ImGui::CheckboxFlags(
"Target", &mask, eModTarget);
248 if (ImGui::Button(
"Load Module"))
253 file_browser.Display();
255 if (file_browser.HasSelected())
257 for (
const auto &file : file_browser.GetMultiSelected())
263 loader.add_module(mod);
271 file_browser.ClearSelected();
274 if (error.length() > 0)
276 ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f),
"Load Error: %s", error.c_str());
279 if (os_error.length() > 0)
281 ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f),
"OS Error: %s", os_error.c_str());
329 static std::vector<Compiler> gCompilers;
333 static constexpr
size_t kMaxPanels = 1024;
345 auto& get_loader_panel()
348 return static_module_panel;
350 return dynamic_module_panel;
354 std::vector<ed::menu_t> menus;
358 menus.push_back(menu);
361 std::vector<std::unique_ptr<ed::SourceView>> sources;
363 void draw_source_files()
365 for (
auto &source : sources)
377 file_browser.SetTitle(
"Open Source Files");
382 draw_loader_window();
384 draw_compiler_tabs();
386 for (
auto& menu : menus)
388 for (
auto& panel : menu.
header)
390 panel->draw_window();
396 for (
auto& panel : section.panels)
398 panel->draw_window();
404 auto& loader_panel = get_loader_panel();
406 loader_panel.draw_window();
410 void draw_module_loader()
412 auto& loader_panel = get_loader_panel();
416 static constexpr ImGuiWindowFlags kMainFlags
417 = ImGuiWindowFlags_NoDecoration
418 | ImGuiWindowFlags_NoMove;
420 ImGui::FileBrowser file_browser { ImGuiFileBrowserFlags_MultipleSelection | ImGuiFileBrowserFlags_ConfirmOnEnter };
422 void draw_loader_window()
424 auto& panel = get_loader_panel();
425 if (!panel.is_enabled())
428 const ImGuiViewport *viewport = ImGui::GetMainViewport();
431 ImGui::SetNextWindowPos(
432 ImVec2(viewport->Pos + viewport->Size / 2.f),
436 if (ImGui::Begin(
"Loader",
nullptr, kMainFlags))
443 void draw_compiler_tabs()
445 if (ImGui::Begin(
"Compiler"))
447 if (ImGui::BeginTabBar(
"CompilerTabs"))
449 if (ImGui::TabItemButton(
"+", ImGuiTabItemFlags_NoTooltip | ImGuiTabItemFlags_Trailing))
452 size_t index = SIZE_MAX;
454 for (
size_t i = 0; i < sources.size(); i++)
457 auto &source = sources[i];
458 if (ImGui::BeginTabItem(source->get_basename(), &open))
460 source->draw_content();
470 if (index != SIZE_MAX)
473 sources.erase(sources.begin() + (ptrdiff_t)index);
481 file_browser.Display();
483 if (file_browser.HasSelected())
485 for (
const fs::path &file : file_browser.GetMultiSelected())
487 sources.emplace_back(std::make_unique<ed::SourceView>(file));
490 file_browser.ClearSelected();
495 static void draw_log_event(
const event_t *event)
497 ImGui::TableNextRow();
498 ImGui::TableNextColumn();
502 ImGui::TableNextColumn();
507 ImGui::TableNextColumn();
508 ImGui::TextUnformatted(event->
message);
510 ImGui::TableNextColumn();
514 ImGui::TextUnformatted(
"None");
517 static const ImGuiTableFlags kLogTableFlags
518 = ImGuiTableFlags_BordersV
519 | ImGuiTableFlags_BordersOuterH
520 | ImGuiTableFlags_Resizable
521 | ImGuiTableFlags_RowBg
522 | ImGuiTableFlags_NoHostExtendX
523 | ImGuiTableFlags_NoBordersInBody
524 | ImGuiTableFlags_ScrollY;
526 static void draw_log_content(
logger_t *logger)
530 ImGui::Text(
"Events: %zu", len);
532 if (ImGui::BeginTable(
"Events", 4, kLogTableFlags))
534 ImGui::TableSetupColumn(
"Diagnostic");
535 ImGui::TableSetupColumn(
"Where");
536 ImGui::TableSetupColumn(
"Message");
537 ImGui::TableSetupColumn(
"Related");
538 ImGui::TableSetupScrollFreeze(0, 1);
539 ImGui::TableHeadersRow();
541 for (
size_t i = 0; i < len; i++)
548 static ImGui::FileBrowser gOpenFile { ImGuiFileBrowserFlags_MultipleSelection | ImGuiFileBrowserFlags_ConfirmOnEnter | ImGuiFileBrowserFlags_CloseOnEsc };
554 case eJsonNull:
return "Null";
555 case eJsonBoolean:
return "Boolean";
556 case eJsonInteger:
return "Integer";
557 case eJsonFloat:
return "Float";
558 case eJsonString:
return "String";
559 case eJsonArray:
return "Array";
560 case eJsonObject:
return "Object";
561 default:
return "Unknown";
570 ImGui::TextUnformatted(mpz_get_str(buffer, 10, digit));
574 static const ImGuiTreeNodeFlags kGroupNodeFlags
575 = ImGuiTreeNodeFlags_SpanAllColumns
576 | ImGuiTreeNodeFlags_AllowOverlap;
578 static const ImGuiTreeNodeFlags kValueNodeFlags
580 | ImGuiTreeNodeFlags_Leaf
581 | ImGuiTreeNodeFlags_Bullet
582 | ImGuiTreeNodeFlags_NoTreePushOnOpen;
584 static void draw_json_item(
const std::string& key,
const ctu::json::Json& value);
586 static void draw_json_array(
const std::string& key,
const ctu::json::Json& value)
588 bool is_open = ImGui::TreeNodeEx(key.c_str(), kGroupNodeFlags,
"%s", key.c_str());
590 ImGui::TableNextColumn();
591 ImGui::TextUnformatted(
"Array");
593 ImGui::TableNextColumn();
596 for (
size_t i = 0; i < value.
length(); i++)
598 draw_json_item(std::format(
"[{}]", i).c_str(), value.
get(i));
604 static void draw_json_object(
const std::string& key,
const ctu::json::Json&
object)
606 bool is_open = ImGui::TreeNodeEx(key.c_str(), kGroupNodeFlags,
"%s", key.c_str());
608 ImGui::TableNextColumn();
609 ImGui::TextUnformatted(
"Object");
611 ImGui::TableNextColumn();
615 auto iter =
object.as_object().iter();
616 while (iter.has_next())
618 auto [entry, value] = iter.next();
619 draw_json_item(std::string{entry}, value);
625 static void draw_json_value(
const std::string& key,
const ctu::json::Json& value)
627 ImGui::TreeNodeEx(key.c_str(), kValueNodeFlags,
"%s", key.c_str());
629 ImGui::TableNextColumn();
630 ImGui::TextUnformatted(get_kind_name(value.
get_kind()));
632 ImGui::TableNextColumn();
636 ImGui::TextUnformatted(
"null");
640 ImGui::TextUnformatted(value.
as_bool() ?
"true" :
"false");
645 draw_json_number(value);
649 std::string_view text = value.
as_string();
650 ImGui::TextUnformatted(text.data(), text.data() + text.size());
658 static void draw_json_item(
const std::string& key,
const ctu::json::Json& value)
660 ImGui::TableNextRow();
661 ImGui::TableNextColumn();
665 draw_json_object(key, value);
669 draw_json_array(key, value);
673 draw_json_value(key, value);
677 static const ImGuiTableFlags kTreeTableFlags
678 = ImGuiTableFlags_BordersV
679 | ImGuiTableFlags_BordersOuterH
680 | ImGuiTableFlags_Resizable
681 | ImGuiTableFlags_RowBg
682 | ImGuiTableFlags_NoHostExtendX
683 | ImGuiTableFlags_NoBordersInBody
684 | ImGuiTableFlags_ScrollY;
688 if (ImGui::BeginTable(
"Document", 3, kTreeTableFlags))
690 ImGui::TableSetupColumn(
"Key");
691 ImGui::TableSetupColumn(
"Type");
692 ImGui::TableSetupColumn(
"Value");
693 ImGui::TableSetupScrollFreeze(0, 1);
694 ImGui::TableHeadersRow();
696 draw_json_item(
"$", value);
721 const void *data =
io_map(
io, eOsProtectRead);
723 source = std::string_view(
static_cast<const char*
>(data),
size);
741 ImGui::Text(
"Failed to open file: %s",
error.
what());
747 if (ImGui::BeginChild(
"JsonSource", ImVec2(200, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX))
754 if (ImGui::BeginChild(
"JsonDocument", ImVec2(0, 0), ImGuiChildFlags_Border))
762 ImGui::Text(
"Failed to parse JSON");
790 if (ImGui::Begin(
"JSON Query", &
query.visible))
792 auto *doc =
query.document;
795 ImGui::TextDisabled(
"No document selected");
800 ImGui::InputTextMultiline(
"Query", &
query.query);
801 if (ImGui::Button(
"Execute"))
806 if (!
query.result.is_valid())
808 ImGui::TextUnformatted(
"Failed to execute query");
809 draw_log_content(
query.logger);
813 ImGui::TextUnformatted(
"Query Result");
814 draw_json(
query.result);
822 if (ImGui::Begin(
"JSON Viewer", &
visible, ImGuiWindowFlags_MenuBar))
824 if (ImGui::BeginMenuBar())
826 if (ImGui::BeginMenu(
"File"))
828 if (ImGui::MenuItem(
"Open File"))
836 if (ImGui::BeginMenu(
"Query"))
838 if (ImGui::MenuItem(
"New Query"))
840 query.visible =
true;
848 if (ImGui::BeginTabBar(
"JsonTabs"))
850 if (ImGui::BeginTabItem(
"Logs"))
858 if (ImGui::BeginTabItem(doc.basename.c_str()))
860 query.document = &doc;
883 void add(
const fs::path &path)
892 static bool gImGuiDemoWindow =
false;
893 static bool gImPlotDemoWindow =
false;
895 static void DrawEditorWidgets()
897 if (gImGuiDemoWindow)
899 ImGui::ShowDemoWindow(&gImGuiDemoWindow);
902 if (gImPlotDemoWindow)
904 ImPlot::ShowDemoWindow(&gImPlotDemoWindow);
907 for (
auto &widget : gTraceWidgets)
909 widget.draw_window();
912 for (
Compiler& instance : gCompilers)
914 instance.draw_window();
919 if (gOpenFile.HasSelected())
921 for (
const fs::path &file : gOpenFile.GetMultiSelected())
923 gJsonEditor.
add(file);
926 gOpenFile.ClearSelected();
935 static void DrawFileMenu()
937 if (ImGui::MenuItem(
"New File"))
944 if (ImGui::MenuItem(
"Open File"))
949 if (ImGui::MenuItem(
"Open Folder"))
955 if (ImGui::MenuItem(
"Exit"))
961 static void DrawEditMenu()
963 ImGui::MenuItem(
"Undo",
"Ctrl+Z");
964 ImGui::MenuItem(
"Redo",
"Ctrl+Y");
966 ImGui::MenuItem(
"Cut",
"Ctrl+X");
967 ImGui::MenuItem(
"Copy",
"Ctrl+C");
968 ImGui::MenuItem(
"Paste",
"Ctrl+V");
970 ImGui::MenuItem(
"Find",
"Ctrl+F");
971 ImGui::MenuItem(
"Replace",
"Ctrl+H");
983 static void DrawViewMenu()
987 for (
auto &widget : gTraceWidgets)
989 ImGui::MenuItem(widget.get_title(),
nullptr, &widget.visible);
992 ImGui::MenuItem(
"JSON Editor",
nullptr, &gJsonWidget.
visible, !gJsonEditor.
documents.empty());
995 ImGui::SetItemTooltip(
"Open a JSON file to enable this widget");
1003 ImGui::SetItemTooltip(
"reconfigure with -DCTU_TRACE_MEMORY=enabled");
1007 ImGui::MenuItem(
"ImGui Demo",
nullptr, &gImGuiDemoWindow);
1008 ImGui::MenuItem(
"ImPlot Demo",
nullptr, &gImPlotDemoWindow);
1010 ImGui::SeparatorText(
"Theme");
1011 if (ImGui::MenuItem(
"Dark",
nullptr, gStyle ==
eStyleDark))
1013 ImGui::StyleColorsDark();
1014 ImPlot::StyleColorsDark();
1018 if (ImGui::MenuItem(
"Light",
nullptr, gStyle ==
eStyleLight))
1020 ImGui::StyleColorsLight();
1021 ImPlot::StyleColorsLight();
1025 if (ImGui::MenuItem(
"Classic",
nullptr, gStyle ==
eStyleClassic))
1027 ImGui::StyleColorsClassic();
1028 ImPlot::StyleColorsClassic();
1033 static void DrawHelpMenu()
1035 ImGui::MenuItem(
"About");
1036 ImGui::MenuItem(
"Documentation");
1038 ImGui::MenuItem(
"Search menus");
1040 ImGui::MenuItem(
"Report Issue");
1041 ImGui::MenuItem(
"Check for Updates");
1044 static void DrawMainMenuBar(
const char *title)
1046 if (ImGui::BeginMainMenuBar())
1048 ImGui::TextUnformatted(title);
1051 if (ImGui::BeginMenu(
"File"))
1057 if (ImGui::BeginMenu(
"Edit"))
1063 if (ImGui::BeginMenu(
"View"))
1069 if (ImGui::BeginMenu(
"Help"))
1075 ImGui::EndMainMenuBar();
1079 int main(
int argc,
const char **argv)
1085 install_trace_arenas();
1088 gCompilers.emplace_back(
Compiler(trace,
"Compiler"));
1090 gOpenFile.SetTitle(
"Open File");
1091 gOpenFile.SetTypeFilters({
".json" });
1105 ImGui::DockSpaceOverViewport();
1106 DrawMainMenuBar(
"Cthulhu");
1107 DrawEditorWidgets();
void draw_content() override
DynamicModulePanel(EditorModulePanel &loader, support_t *support)
void add_module(const loaded_module_t &mod)
bool menu_item(const char *shortcut=nullptr) override
void load_default_modules(support_t *support)
StaticModulePanel(EditorModulePanel &loader, support_t *support)
bool menu_item(const char *shortcut=nullptr) override
void draw_content() override
const char * what() const
logger_t * get_logger() const
get the logger
Json parse(io_t *io)
parse a json value parse the contents of an io object into a json value
a json value a json node from a parsed json document
CT_PUREFN bool is_object() const
check if the value is an object
constexpr CT_PUREFN bool is_valid() const
check if the value is valid this should always be checked before using any other methods
CT_PUREFN std::string_view as_string() const
get the string value
void as_integer(mpz_t integer) const
get the integer value
CT_PUREFN bool as_bool() const
get the boolean value
Json get(const char *key) const
get a value from an object by key
CT_PUREFN bool is_array() const
check if the value is an array
CT_PUREFN json_kind_t get_kind() const
get the kind of the value
CT_PUREFN size_t length() const
get the length of an array
virtual bool menu_item(const char *shortcut=nullptr)
void set_enabled(bool value)
IEditorPanel(std::string_view name)
CT_NODISCARD CT_PUREFN CT_SCAN_API const char * scan_path(const scan_t *scan)
get the path of a scanner
CT_NODISCARD STA_RET_STRING CT_OS_API char * os_error_string(os_error_t error, arena_t *arena)
convert an os error code to a string
int main(int argc, const char **argv)
void draw_menu_items(ed::menu_section_t §ion)
CT_BACKTRACE_API void bt_update(void)
update the loaded module cache
CT_BROKER_API broker_t * broker_new(const frontend_t *frontend, arena_t *arena)
#define CT_NEW_VERSION(major, minor, patch)
creates a new ctu_version_t from major, minor and patch
CT_MEMORY_API void init_gmp_arena(arena_t *arena)
initialize gmp with a custom allocator
CT_MEMORY_API void init_global_arena(arena_t *arena)
initialize the global memory arena
CT_MEMORY_API arena_t * get_global_arena(void)
get the global memory arena
CT_NODISCARD CT_IO_API io_t * io_file(const char *path, os_access_t mode, arena_t *arena)
create an IO object from a file
CT_NODISCARD CT_IO_API os_error_t io_error(const io_t *io)
get the last error from the io object
CT_NODISCARD CT_IO_API size_t io_size(io_t *io)
get the total size of an io objects contents
CT_NODISCARD CT_IO_API void * io_map(io_t *io, os_protect_t protect)
map an io object into memory maps an io objects entire contents into memory.
json_kind_t
the kind of json value
CT_SUPPORT_API void support_load_default_modules(support_t *support)
load all default modules
CT_SUPPORT_API bool support_load_module(support_t *support, module_type_t mask, const char *name, loaded_module_t *out)
CT_SUPPORT_API typevec_t * support_get_modules(support_t *support)
CT_SUPPORT_API support_t * support_new(broker_t *broker, loader_t *loader, arena_t *arena)
create a support instance from an existing loader and broker configures the broker with the modules i...
CT_NODISCARD CT_PUREFN CT_SCAN_API const scan_t * node_get_scan(const node_t *node)
get the associated source file of a node
CT_NODISCARD CT_PUREFN CT_SCAN_API where_t node_get_location(const node_t *node)
get the location of a node inside its source file
#define CT_UNUSED(x)
mark a variable as unused
CT_ARENA_API void arena_opt_free(STA_RELEASE void *ptr, size_t size, arena_t *arena)
release memory from a custom allocator
#define CTU_TRACE_MEMORY
a compile time flag to enable memory tracing
#define ARENA_OPT_MALLOC(size, name, parent, arena)
#define CT_ALLOC_SIZE_UNKNOWN
unknown allocation size constant when freeing or reallocating memory, this can be used as the size to...
CT_NODISCARD CT_NOTIFY_API logger_t * logger_new(arena_t *arena)
create a new logger
CT_NODISCARD CT_NOTIFY_API typevec_t * logger_get_events(const logger_t *logs)
get the events from the logger
CT_SETUP_API void setup_default(arena_t *arena)
initialise the runtime with default options
CT_NODISCARD CT_PUREFN CT_STD_API size_t typevec_len(const typevec_t *vec)
get the length of a vector
CT_STD_API void typevec_get(const typevec_t *vec, size_t index, STA_WRITES(vec->width) void *dst)
get an element from the vector
CT_NODISCARD CT_PUREFN CT_STD_API void * typevec_offset(const typevec_t *vec, size_t index)
get a pointer to the value at the given index
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
CT_SUPPORT_API loader_t * loader_new(arena_t *arena)
CT_SUPPORT_API const char * load_error_string(load_error_t error)
bool create(const config_t &config)
bool draw_collapsing(IEditorPanel &panel, const char *title=nullptr, ImGuiTreeNodeFlags flags=ImGuiTreeNodeFlags_None)
Compiler(TraceArena &trace, std::string_view title)
void add(const fs::path &path)
ctu::json::JsonParser parser
std::vector< JsonFile > documents
JsonFile(const fs::path &fp, ctu::json::JsonParser &parser, arena_t *arena)
void draw_json_document()
const char * id
the id of the diagnostic should be in the format [A-Z]{2,3}[0-9]{4} e.g. CLI0001 this is not enforced...
an event handle TODO: make this and segment_t opaque
node_t node
the primary node that this event is attached to
const diagnostic_t * diagnostic
the related diagnostic
vector_t * notes
extra notes that this event is attached to
STA_FIELD_STRING char * message
the primary message
the frontend running the mediator
module_info_t info
information about the frontend
const char * id
unique id for the module
A vector with a fixed type size.
a location inside a scanner locations are inclusive and 0-based
ctu_column_t first_column
the first column of the location
ctu_line_t first_line
the first line of the location
CT_JSON_API json_t * json_query(json_t *json, const char *query, logger_t *logger, arena_t *arena)
query a json object
#define PRI_LINE
format specifier for ctu_line_t
#define PRI_COLUMN
format specifier for ctu_column_t