Cthulhu  0.2.10
Cthulhu compiler collection
main.cpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-3.0-only
2 #include "notify/notify.h"
3 #include "scan/node.h"
4 #include "std/vector.h"
5 #include "stdafx.hpp"
6 
7 // drawing library
8 
9 #include "draw/draw.hpp"
10 
11 // editor functionality
12 
13 #include "editor/compile.hpp"
14 #include "editor/panels/info.hpp"
15 #include "editor/panels/arena.hpp"
16 
17 // editor panels
18 
20 
21 // cthulhu includes
22 
23 #include "json/json.hpp"
24 #include "json/query.h"
25 #include "backtrace/backtrace.h"
26 #include "setup/setup.h"
27 
28 #include "support/loader.h"
29 #include "support/support.h"
30 
31 #include "memory/memory.h"
32 #include "config/config.h"
33 
34 #include "std/typed/vector.h"
35 
36 #include "core/macros.h"
37 
38 namespace fs = std::filesystem;
39 
40 static const frontend_t kFrontendGui = {
41  .info = {
42  .id = "frontend/gui",
43  .name = "Cthulhu GUI",
44  .version = {
45  .license = "GPLv3",
46  .desc = "Cthulhu Compiler Collection GUI",
47  .author = "Elliot Haisley",
48  .version = CT_NEW_VERSION(0, 0, 1),
49  },
50  },
51 };
52 
53 static TraceArena gGlobalArena{"Global Arena", TraceArena::eCollectStackTrace};
54 static TraceArena gGmpArena{"GMP Arena", TraceArena::eCollectStackTrace};
55 static TraceArena gGuiArena{"Dear ImGui Arena", TraceArena::eCollectNone};
56 
57 static std::vector<TraceArenaWidget> gTraceWidgets = {
58  { gGlobalArena, TraceArenaWidget::eDrawTree },
59  { gGmpArena, TraceArenaWidget::eDrawFlat },
60  { gGuiArena, TraceArenaWidget::eDrawFlat },
61 };
62 
63 static void install_trace_arenas()
64 {
65  init_global_arena(gGlobalArena.get_arena());
66  init_gmp_arena(gGmpArena.get_arena());
67 
68  ImGui::SetAllocatorFunctions(
69  /*alloc_func=*/ [](size_t size, void *user) {
70  arena_t *arena = static_cast<arena_t*>(user);
71  return ARENA_OPT_MALLOC(size, "ImGui::MemAlloc", nullptr, arena);
72  },
73  /*free_func=*/ [](void *ptr, void *user) {
74  arena_t *arena = static_cast<arena_t*>(user);
76  },
77  /*user_data=*/ gGuiArena.get_arena()
78  );
79 }
80 
82 {
83  std::vector<ed::LanguageInfoPanel> languages;
84  std::vector<ed::PluginInfoPanel> plugins;
85  std::vector<ed::TargetInfoPanel> targets;
86 
87  void draw_content() override
88  {
89  if (ImGui::BeginTabBar("ModuleTabs"))
90  {
91  if (ImGui::BeginTabItem("Languages"))
92  {
93  for (auto &lang : languages)
94  {
95  ed::draw_collapsing(lang);
96  }
97 
98  ImGui::EndTabItem();
99  }
100 
101  if (ImGui::BeginTabItem("Plugins"))
102  {
103  for (auto &plugin : plugins)
104  {
105  ed::draw_collapsing(plugin);
106  }
107 
108  ImGui::EndTabItem();
109  }
110 
111  if (ImGui::BeginTabItem("Targets"))
112  {
113  for (auto &target : targets)
114  {
115  ed::draw_collapsing(target);
116  }
117 
118  ImGui::EndTabItem();
119  }
120 
121  ImGui::EndTabBar();
122  }
123  }
124 
125 public:
127  : IEditorPanel("Modules")
128  {
129  set_enabled(false);
130  }
131 
132  void add_module(const loaded_module_t &mod)
133  {
134  if (mod.type & eModLanguage)
135  {
136  languages.emplace_back(*mod.lang);
137  }
138 
139  if (mod.type & eModPlugin)
140  {
141  plugins.emplace_back(*mod.plugin);
142  }
143 
144  if (mod.type & eModTarget)
145  {
146  targets.emplace_back(*mod.target);
147  }
148  }
149 
151  {
153 
154  typevec_t *mods = support_get_modules(support);
155  size_t len = typevec_len(mods);
156 
157  for (size_t i = 0; i < len; i++)
158  {
159  loaded_module_t mod = {};
160  typevec_get(mods, i, &mod);
161  add_module(mod);
162  }
163 
164  set_enabled(true);
165  }
166 
167  bool menu_item(const char *shortcut = nullptr) override
168  {
169  bool result = IEditorPanel::menu_item(shortcut);
170  if (!enabled)
171  ImGui::SetItemTooltip("Load a module to enable this panel");
172 
173  return result;
174  }
175 
176  bool is_empty() const
177  {
178  return languages.empty() && plugins.empty() && targets.empty();
179  }
180 };
181 
183 {
184  EditorModulePanel& loader;
185  support_t *support;
186 
187 public:
189  : IEditorPanel("Loader")
190  , loader(loader)
191  , support(support)
192  { }
193 
194  void draw_content() override
195  {
196  if (loader.is_empty())
197  {
198  if (ImGui::Button("Load default modules"))
199  {
200  loader.load_default_modules(support);
201  visible = false;
202  enabled = false;
203  }
204  }
205  else
206  {
207  ImGui::Text("Default modules loaded");
208  }
209  }
210 
211  bool menu_item(const char *shortcut = nullptr) override
212  {
213  if (!loader.is_empty())
214  return false;
215 
216  return IEditorPanel::menu_item(shortcut);
217  }
218 };
219 
221 {
222  EditorModulePanel& loader;
223  support_t *support;
224 
225  std::string path;
226  int mask = eModLanguage;
227 
228  ImGui::FileBrowser file_browser { ImGuiFileBrowserFlags_MultipleSelection | ImGuiFileBrowserFlags_ConfirmOnEnter };
229 
230  std::string error;
231  std::string os_error;
232 
233 public:
235  : IEditorPanel("Loader")
236  , loader(loader)
237  , support(support)
238  {
239  file_browser.SetTitle("Open Shared Module");
240  file_browser.SetTypeFilters({ ".dll", ".so", ".dylib" });
241  }
242 
243  void draw_content() override
244  {
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"))
249  {
250  file_browser.Open();
251  }
252 
253  file_browser.Display();
254 
255  if (file_browser.HasSelected())
256  {
257  for (const auto &file : file_browser.GetMultiSelected())
258  {
259  loaded_module_t mod = {};
260  if (support_load_module(support, module_type_t(mask), file.string().c_str(), &mod))
261  {
262  bt_update();
263  loader.add_module(mod);
264  }
265  else
266  {
267  error = load_error_string(mod.error);
268  os_error = os_error_string(mod.os, get_global_arena());
269  }
270  }
271  file_browser.ClearSelected();
272  }
273 
274  if (error.length() > 0)
275  {
276  ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "Load Error: %s", error.c_str());
277  }
278 
279  if (os_error.length() > 0)
280  {
281  ImGui::TextColored(ImVec4(1.f, 0.f, 0.f, 1.f), "OS Error: %s", os_error.c_str());
282  }
283  }
284 };
285 
287 {
288  for (ed::IEditorPanel *panel : section.panels)
289  {
290  panel->menu_item();
291  }
292 }
293 
294 struct Compiler
295 {
296  std::string name;
301 
302  Compiler(TraceArena& trace, std::string_view title)
303  : name(title)
304  , arena(trace)
305  , loader(loader_new(arena.get_arena()))
306  , broker(broker_new(&kFrontendGui, arena.get_arena()))
307  , support(support_new(broker, loader, arena.get_arena()))
308  {
310 
311  gTraceWidgets.push_back({ arena, TraceArenaWidget::eDrawTree });
312  }
313 
314  bool visible = false;
315 
316  void draw_window()
317  {
318  if (!visible)
319  return;
320 
321  if (ImGui::Begin(name.c_str(), &visible))
322  {
323 
324  }
325  ImGui::End();
326  }
327 };
328 
329 static std::vector<Compiler> gCompilers;
330 
331 class EditorUi
332 {
333  static constexpr size_t kMaxPanels = 1024;
334 
335  loader_t *loader;
336  broker_t *broker;
337  support_t *support;
338 
339  ed::FrontendInfoPanel version_info_panel { kFrontendGui };
340 
341  EditorModulePanel module_panel;
342  DynamicModulePanel dynamic_module_panel { module_panel, support };
343  StaticModulePanel static_module_panel { module_panel, support };
344 
345  auto& get_loader_panel()
346  {
347 #if CT_BUILD_STATIC
348  return static_module_panel;
349 #else
350  return dynamic_module_panel;
351 #endif
352  }
353 
354  std::vector<ed::menu_t> menus;
355 
356  void add_menu(const ed::menu_t &menu)
357  {
358  menus.push_back(menu);
359  }
360 
361  std::vector<std::unique_ptr<ed::SourceView>> sources;
362 
363  void draw_source_files()
364  {
365  for (auto &source : sources)
366  {
367  source->draw_content();
368  }
369  }
370 
371 public:
373  : loader(loader_new(get_global_arena()))
374  , broker(broker_new(&kFrontendGui, get_global_arena()))
375  , support(support_new(broker, loader, get_global_arena()))
376  {
377  file_browser.SetTitle("Open Source Files");
378  }
379 
381  {
382  draw_loader_window();
383 
384  draw_compiler_tabs();
385 
386  for (auto& menu : menus)
387  {
388  for (auto& panel : menu.header)
389  {
390  panel->draw_window();
391  panel->update();
392  }
393 
394  for (auto& section : menu.sections)
395  {
396  for (auto& panel : section.panels)
397  {
398  panel->draw_window();
399  panel->update();
400  }
401  }
402  }
403 
404  auto& loader_panel = get_loader_panel();
405  if (!module_panel.is_empty())
406  loader_panel.draw_window();
407  }
408 
409 private:
410  void draw_module_loader()
411  {
412  auto& loader_panel = get_loader_panel();
413  loader_panel.draw();
414  }
415 
416  static constexpr ImGuiWindowFlags kMainFlags
417  = ImGuiWindowFlags_NoDecoration
418  | ImGuiWindowFlags_NoMove;
419 
420  ImGui::FileBrowser file_browser { ImGuiFileBrowserFlags_MultipleSelection | ImGuiFileBrowserFlags_ConfirmOnEnter };
421 
422  void draw_loader_window()
423  {
424  auto& panel = get_loader_panel();
425  if (!panel.is_enabled())
426  return;
427 
428  const ImGuiViewport *viewport = ImGui::GetMainViewport();
429 
430  // center the window
431  ImGui::SetNextWindowPos(
432  ImVec2(viewport->Pos + viewport->Size / 2.f),
433  ImGuiCond_Appearing,
434  ImVec2(0.5f, 0.5f));
435 
436  if (ImGui::Begin("Loader", nullptr, kMainFlags))
437  {
438  panel.draw();
439  }
440  ImGui::End();
441  }
442 
443  void draw_compiler_tabs()
444  {
445  if (ImGui::Begin("Compiler"))
446  {
447  if (ImGui::BeginTabBar("CompilerTabs"))
448  {
449  if (ImGui::TabItemButton("+", ImGuiTabItemFlags_NoTooltip | ImGuiTabItemFlags_Trailing))
450  file_browser.Open();
451 
452  size_t index = SIZE_MAX;
453 
454  for (size_t i = 0; i < sources.size(); i++)
455  {
456  bool open = true;
457  auto &source = sources[i];
458  if (ImGui::BeginTabItem(source->get_basename(), &open))
459  {
460  source->draw_content();
461  ImGui::EndTabItem();
462  }
463 
464  if (!open)
465  {
466  index = i;
467  }
468  }
469 
470  if (index != SIZE_MAX)
471  {
472  // TODO: remove the source file
473  sources.erase(sources.begin() + (ptrdiff_t)index);
474  }
475 
476  ImGui::EndTabBar();
477  }
478  }
479  ImGui::End();
480 
481  file_browser.Display();
482 
483  if (file_browser.HasSelected())
484  {
485  for (const fs::path &file : file_browser.GetMultiSelected())
486  {
487  sources.emplace_back(std::make_unique<ed::SourceView>(file));
488  }
489 
490  file_browser.ClearSelected();
491  }
492  }
493 };
494 
495 static void draw_log_event(const event_t *event)
496 {
497  ImGui::TableNextRow();
498  ImGui::TableNextColumn();
499 
500  ImGui::TextUnformatted(event->diagnostic->id);
501 
502  ImGui::TableNextColumn();
503  where_t where = node_get_location(&event->node);
504  const scan_t *scan = node_get_scan(&event->node);
505  ImGui::Text("%s:%" PRI_LINE ":%" PRI_COLUMN, scan_path(scan), where.first_line, where.first_column);
506 
507  ImGui::TableNextColumn();
508  ImGui::TextUnformatted(event->message);
509 
510  ImGui::TableNextColumn();
511  if (event->notes)
512  ImGui::Text("%zu notes", vector_len(event->notes));
513  else
514  ImGui::TextUnformatted("None");
515 }
516 
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;
525 
526 static void draw_log_content(logger_t *logger)
527 {
528  typevec_t *events = logger_get_events(logger);
529  size_t len = typevec_len(events);
530  ImGui::Text("Events: %zu", len);
531 
532  if (ImGui::BeginTable("Events", 4, kLogTableFlags))
533  {
534  ImGui::TableSetupColumn("Diagnostic");
535  ImGui::TableSetupColumn("Where");
536  ImGui::TableSetupColumn("Message");
537  ImGui::TableSetupColumn("Related");
538  ImGui::TableSetupScrollFreeze(0, 1);
539  ImGui::TableHeadersRow();
540 
541  for (size_t i = 0; i < len; i++)
542  draw_log_event((event_t*)typevec_offset(events, i));
543 
544  ImGui::EndTable();
545  }
546 }
547 
548 static ImGui::FileBrowser gOpenFile { ImGuiFileBrowserFlags_MultipleSelection | ImGuiFileBrowserFlags_ConfirmOnEnter | ImGuiFileBrowserFlags_CloseOnEsc };
549 
550 static const char *get_kind_name(json_kind_t kind)
551 {
552  switch (kind)
553  {
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";
562  }
563 }
564 
565 static void draw_json_number(const ctu::json::Json& value)
566 {
567  mpz_t digit;
568  value.as_integer(digit);
569  char buffer[1024];
570  ImGui::TextUnformatted(mpz_get_str(buffer, 10, digit));
571  mpz_clear(digit);
572 }
573 
574 static const ImGuiTreeNodeFlags kGroupNodeFlags
575  = ImGuiTreeNodeFlags_SpanAllColumns
576  | ImGuiTreeNodeFlags_AllowOverlap;
577 
578 static const ImGuiTreeNodeFlags kValueNodeFlags
579  = kGroupNodeFlags
580  | ImGuiTreeNodeFlags_Leaf
581  | ImGuiTreeNodeFlags_Bullet
582  | ImGuiTreeNodeFlags_NoTreePushOnOpen;
583 
584 static void draw_json_item(const std::string& key, const ctu::json::Json& value);
585 
586 static void draw_json_array(const std::string& key, const ctu::json::Json& value)
587 {
588  bool is_open = ImGui::TreeNodeEx(key.c_str(), kGroupNodeFlags, "%s", key.c_str());
589 
590  ImGui::TableNextColumn();
591  ImGui::TextUnformatted("Array");
592 
593  ImGui::TableNextColumn();
594  if (is_open)
595  {
596  for (size_t i = 0; i < value.length(); i++)
597  {
598  draw_json_item(std::format("[{}]", i).c_str(), value.get(i));
599  }
600  ImGui::TreePop();
601  }
602 }
603 
604 static void draw_json_object(const std::string& key, const ctu::json::Json& object)
605 {
606  bool is_open = ImGui::TreeNodeEx(key.c_str(), kGroupNodeFlags, "%s", key.c_str());
607 
608  ImGui::TableNextColumn();
609  ImGui::TextUnformatted("Object");
610 
611  ImGui::TableNextColumn();
612  if (is_open)
613  {
614  // TODO: make iterators work
615  auto iter = object.as_object().iter();
616  while (iter.has_next())
617  {
618  auto [entry, value] = iter.next();
619  draw_json_item(std::string{entry}, value);
620  }
621  ImGui::TreePop();
622  }
623 }
624 
625 static void draw_json_value(const std::string& key, const ctu::json::Json& value)
626 {
627  ImGui::TreeNodeEx(key.c_str(), kValueNodeFlags, "%s", key.c_str());
628 
629  ImGui::TableNextColumn();
630  ImGui::TextUnformatted(get_kind_name(value.get_kind()));
631 
632  ImGui::TableNextColumn();
633  switch (value.get_kind())
634  {
635  case eJsonNull:
636  ImGui::TextUnformatted("null");
637  break;
638 
639  case eJsonBoolean:
640  ImGui::TextUnformatted(value.as_bool() ? "true" : "false");
641  break;
642 
643  case eJsonInteger:
644  case eJsonFloat:
645  draw_json_number(value);
646  break;
647 
648  case eJsonString: {
649  std::string_view text = value.as_string();
650  ImGui::TextUnformatted(text.data(), text.data() + text.size());
651  break;
652  }
653  default:
654  break;
655  }
656 }
657 
658 static void draw_json_item(const std::string& key, const ctu::json::Json& value)
659 {
660  ImGui::TableNextRow();
661  ImGui::TableNextColumn();
662 
663  if (value.is_object())
664  {
665  draw_json_object(key, value);
666  }
667  else if (value.is_array())
668  {
669  draw_json_array(key, value);
670  }
671  else
672  {
673  draw_json_value(key, value);
674  }
675 }
676 
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;
685 
686 static void draw_json(const ctu::json::Json& value)
687 {
688  if (ImGui::BeginTable("Document", 3, kTreeTableFlags))
689  {
690  ImGui::TableSetupColumn("Key");
691  ImGui::TableSetupColumn("Type");
692  ImGui::TableSetupColumn("Value");
693  ImGui::TableSetupScrollFreeze(0, 1);
694  ImGui::TableHeadersRow();
695 
696  draw_json_item("$", value);
697 
698  ImGui::EndTable();
699  }
700 }
701 struct JsonFile
702 {
703  std::string path;
704  std::string basename;
705 
708  std::string_view source;
709 
711 
712  JsonFile(const fs::path& fp, ctu::json::JsonParser& parser, arena_t *arena)
713  : path(fp.string())
714  , basename(fp.filename().string())
715  , io(io_file(path.c_str(), eOsAccessRead, arena))
716  , error(io_error(io))
717  {
718  if (error.failed())
719  return;
720 
721  const void *data = io_map(io, eOsProtectRead);
722  size_t size = io_size(io);
723  source = std::string_view(static_cast<const char*>(data), size);
724  value = parser.parse(io);
725  }
726 
727  void draw_source()
728  {
729  ImGui::TextUnformatted(source.data(), source.data() + source.size());
730  }
731 
733  {
734  draw_json(value);
735  }
736 
738  {
739  if (error.failed())
740  {
741  ImGui::Text("Failed to open file: %s", error.what());
742  return;
743  }
744 
745  if (value.is_valid())
746  {
747  if (ImGui::BeginChild("JsonSource", ImVec2(200, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX))
748  {
749  draw_source();
750  }
751  ImGui::EndChild();
752  ImGui::SameLine();
753 
754  if (ImGui::BeginChild("JsonDocument", ImVec2(0, 0), ImGuiChildFlags_Border))
755  {
757  }
758  ImGui::EndChild();
759  }
760  else
761  {
762  ImGui::Text("Failed to parse JSON");
763  draw_source();
764  }
765  }
766 };
767 
769 {
772  bool visible = false;
773  std::vector<JsonFile> documents;
774 
775  struct {
776  bool visible;
777  std::string query = "$";
778  JsonFile *document = nullptr;
780 
782  } query;
783 
785  query.logger = logger_new(arena.get_arena());
786  }
787 
789  {
790  if (ImGui::Begin("JSON Query", &query.visible))
791  {
792  auto *doc = query.document;
793  if (doc == nullptr)
794  {
795  ImGui::TextDisabled("No document selected");
796  ImGui::End();
797  return;
798  }
799 
800  ImGui::InputTextMultiline("Query", &query.query);
801  if (ImGui::Button("Execute"))
802  {
803  query.result = json_query(doc->value.get_ast(), query.query.c_str(), query.logger, arena.get_arena());
804  }
805 
806  if (!query.result.is_valid())
807  {
808  ImGui::TextUnformatted("Failed to execute query");
809  draw_log_content(query.logger);
810  }
811  else
812  {
813  ImGui::TextUnformatted("Query Result");
814  draw_json(query.result);
815  }
816  }
817  ImGui::End();
818  }
819 
821  {
822  if (ImGui::Begin("JSON Viewer", &visible, ImGuiWindowFlags_MenuBar))
823  {
824  if (ImGui::BeginMenuBar())
825  {
826  if (ImGui::BeginMenu("File"))
827  {
828  if (ImGui::MenuItem("Open File"))
829  {
830  gOpenFile.Open();
831  }
832 
833  ImGui::EndMenu();
834  }
835 
836  if (ImGui::BeginMenu("Query"))
837  {
838  if (ImGui::MenuItem("New Query"))
839  {
840  query.visible = true;
841  }
842 
843  ImGui::EndMenu();
844  }
845  ImGui::EndMenuBar();
846  }
847 
848  if (ImGui::BeginTabBar("JsonTabs"))
849  {
850  if (ImGui::BeginTabItem("Logs"))
851  {
852  draw_log_content(parser.get_logger());
853  ImGui::EndTabItem();
854  }
855 
856  for (auto &doc : documents)
857  {
858  if (ImGui::BeginTabItem(doc.basename.c_str()))
859  {
860  query.document = &doc;
861  doc.draw_content();
862  ImGui::EndTabItem();
863  }
864  }
865  ImGui::EndTabBar();
866  }
867  }
868 
869  ImGui::End();
870  }
871 
872  void draw_window()
873  {
874  if (!visible)
875  return;
876 
878 
879  if (query.visible)
881  }
882 
883  void add(const fs::path &path)
884  {
885  documents.push_back({ path, parser, arena.get_arena() });
886  }
887 };
888 
889 static JsonEditor gJsonEditor;
890 static TraceArenaWidget gJsonWidget{gJsonEditor.arena, TraceArenaWidget::eDrawTree};
891 
892 static bool gImGuiDemoWindow = false;
893 static bool gImPlotDemoWindow = false;
894 
895 static void DrawEditorWidgets()
896 {
897  if (gImGuiDemoWindow)
898  {
899  ImGui::ShowDemoWindow(&gImGuiDemoWindow);
900  }
901 
902  if (gImPlotDemoWindow)
903  {
904  ImPlot::ShowDemoWindow(&gImPlotDemoWindow);
905  }
906 
907  for (auto &widget : gTraceWidgets)
908  {
909  widget.draw_window();
910  }
911 
912  for (Compiler& instance : gCompilers)
913  {
914  instance.draw_window();
915  }
916 
917  gOpenFile.Display();
918 
919  if (gOpenFile.HasSelected())
920  {
921  for (const fs::path &file : gOpenFile.GetMultiSelected())
922  {
923  gJsonEditor.add(file);
924  }
925 
926  gOpenFile.ClearSelected();
927 
928  gJsonEditor.visible = true;
929  }
930 
931  gJsonEditor.draw_window();
932  gJsonWidget.draw_window();
933 }
934 
935 static void DrawFileMenu()
936 {
937  if (ImGui::MenuItem("New File"))
938  {
939 
940  }
941 
942  ImGui::Separator();
943 
944  if (ImGui::MenuItem("Open File"))
945  {
946  gOpenFile.Open();
947  }
948 
949  if (ImGui::MenuItem("Open Folder"))
950  {
951 
952  }
953 
954  ImGui::Separator();
955  if (ImGui::MenuItem("Exit"))
956  {
957  draw::close();
958  }
959 }
960 
961 static void DrawEditMenu()
962 {
963  ImGui::MenuItem("Undo", "Ctrl+Z");
964  ImGui::MenuItem("Redo", "Ctrl+Y");
965  ImGui::Separator();
966  ImGui::MenuItem("Cut", "Ctrl+X");
967  ImGui::MenuItem("Copy", "Ctrl+C");
968  ImGui::MenuItem("Paste", "Ctrl+V");
969  ImGui::Separator();
970  ImGui::MenuItem("Find", "Ctrl+F");
971  ImGui::MenuItem("Replace", "Ctrl+H");
972 }
973 
975 {
979 };
980 
981 static EditorStyle gStyle = eStyleDark;
982 
983 static void DrawViewMenu()
984 {
985  if (ImGui::BeginMenu("Memory Statistics", CTU_TRACE_MEMORY))
986  {
987  for (auto &widget : gTraceWidgets)
988  {
989  ImGui::MenuItem(widget.get_title(), nullptr, &widget.visible);
990  }
991 
992  ImGui::MenuItem("JSON Editor", nullptr, &gJsonWidget.visible, !gJsonEditor.documents.empty());
993  if (gJsonEditor.documents.empty())
994  {
995  ImGui::SetItemTooltip("Open a JSON file to enable this widget");
996  }
997 
998  ImGui::EndMenu();
999  }
1000 
1001  if constexpr (!CTU_TRACE_MEMORY)
1002  {
1003  ImGui::SetItemTooltip("reconfigure with -DCTU_TRACE_MEMORY=enabled");
1004  }
1005 
1006  ImGui::Separator();
1007  ImGui::MenuItem("ImGui Demo", nullptr, &gImGuiDemoWindow);
1008  ImGui::MenuItem("ImPlot Demo", nullptr, &gImPlotDemoWindow);
1009 
1010  ImGui::SeparatorText("Theme");
1011  if (ImGui::MenuItem("Dark", nullptr, gStyle == eStyleDark))
1012  {
1013  ImGui::StyleColorsDark();
1014  ImPlot::StyleColorsDark();
1015  gStyle = eStyleDark;
1016  }
1017 
1018  if (ImGui::MenuItem("Light", nullptr, gStyle == eStyleLight))
1019  {
1020  ImGui::StyleColorsLight();
1021  ImPlot::StyleColorsLight();
1022  gStyle = eStyleLight;
1023  }
1024 
1025  if (ImGui::MenuItem("Classic", nullptr, gStyle == eStyleClassic))
1026  {
1027  ImGui::StyleColorsClassic();
1028  ImPlot::StyleColorsClassic();
1029  gStyle = eStyleClassic;
1030  }
1031 }
1032 
1033 static void DrawHelpMenu()
1034 {
1035  ImGui::MenuItem("About");
1036  ImGui::MenuItem("Documentation");
1037  ImGui::Separator();
1038  ImGui::MenuItem("Search menus");
1039  ImGui::Separator();
1040  ImGui::MenuItem("Report Issue");
1041  ImGui::MenuItem("Check for Updates");
1042 }
1043 
1044 static void DrawMainMenuBar(const char *title)
1045 {
1046  if (ImGui::BeginMainMenuBar())
1047  {
1048  ImGui::TextUnformatted(title);
1049  ImGui::Separator();
1050 
1051  if (ImGui::BeginMenu("File"))
1052  {
1053  DrawFileMenu();
1054  ImGui::EndMenu();
1055  }
1056 
1057  if (ImGui::BeginMenu("Edit"))
1058  {
1059  DrawEditMenu();
1060  ImGui::EndMenu();
1061  }
1062 
1063  if (ImGui::BeginMenu("View"))
1064  {
1065  DrawViewMenu();
1066  ImGui::EndMenu();
1067  }
1068 
1069  if (ImGui::BeginMenu("Help"))
1070  {
1071  DrawHelpMenu();
1072  ImGui::EndMenu();
1073  }
1074 
1075  ImGui::EndMainMenuBar();
1076  }
1077 }
1078 
1079 int main(int argc, const char **argv)
1080 {
1081  CT_UNUSED(argc);
1082  CT_UNUSED(argv);
1083 
1084  setup_default(nullptr);
1085  install_trace_arenas();
1086 
1087  TraceArena trace{"Compiler", TraceArena::eCollectStackTrace};
1088  gCompilers.emplace_back(Compiler(trace, "Compiler"));
1089 
1090  gOpenFile.SetTitle("Open File");
1091  gOpenFile.SetTypeFilters({ ".json" });
1092 
1093  draw::config_t config = {
1094  .title = L"Editor",
1095  .hwaccel = false,
1096  };
1097 
1098  if (!draw::create(config))
1099  {
1100  return 1;
1101  }
1102 
1103  while (draw::begin_frame())
1104  {
1105  ImGui::DockSpaceOverViewport();
1106  DrawMainMenuBar("Cthulhu");
1107  DrawEditorWidgets();
1108  draw::end_frame();
1109  }
1110 
1111  draw::destroy();
1112 }
void draw_content() override
Definition: main.cpp:243
DynamicModulePanel(EditorModulePanel &loader, support_t *support)
Definition: main.cpp:234
void add_module(const loaded_module_t &mod)
Definition: main.cpp:132
bool menu_item(const char *shortcut=nullptr) override
Definition: main.cpp:167
bool is_empty() const
Definition: main.cpp:176
void load_default_modules(support_t *support)
Definition: main.cpp:150
void draw_windows()
Definition: main.cpp:380
EditorUi()
Definition: main.cpp:372
arena_t * get_arena()
Definition: arena.hpp:26
StaticModulePanel(EditorModulePanel &loader, support_t *support)
Definition: main.cpp:188
bool menu_item(const char *shortcut=nullptr) override
Definition: main.cpp:211
void draw_content() override
Definition: main.cpp:194
bool draw_window()
Definition: arena.cpp:555
@ eDrawFlat
draw the allocations as a flat list
Definition: arena.hpp:117
@ eDrawTree
draw the allocations as a tree using parent data
Definition: arena.hpp:114
@ eCollectNone
Definition: arena.hpp:79
@ eCollectStackTrace
Definition: arena.hpp:81
const char * what() const
Definition: io.cpp:34
bool failed() const
Definition: io.cpp:29
a json parser
Definition: json.hpp:354
logger_t * get_logger() const
get the logger
Definition: json.hpp:376
Json parse(io_t *io)
parse a json value parse the contents of an io object into a json value
Definition: json.cpp:138
a json value a json node from a parsed json document
Definition: json.hpp:118
CT_PUREFN bool is_object() const
check if the value is an object
Definition: json.cpp:69
constexpr CT_PUREFN bool is_valid() const
check if the value is valid this should always be checked before using any other methods
Definition: json.hpp:176
CT_PUREFN std::string_view as_string() const
get the string value
Definition: json.cpp:79
void as_integer(mpz_t integer) const
get the integer value
Definition: json.cpp:85
CT_PUREFN bool as_bool() const
get the boolean value
Definition: json.cpp:95
Json get(const char *key) const
get a value from an object by key
Definition: json.cpp:110
CT_PUREFN bool is_array() const
check if the value is an array
Definition: json.cpp:68
CT_PUREFN json_kind_t get_kind() const
get the kind of the value
Definition: json.cpp:74
CT_PUREFN size_t length() const
get the length of an array
Definition: json.cpp:120
virtual bool menu_item(const char *shortcut=nullptr)
Definition: panel.cpp:32
void set_enabled(bool value)
Definition: panel.hpp:35
IEditorPanel(std::string_view name)
Definition: panel.cpp:8
CT_NODISCARD CT_PUREFN CT_SCAN_API const char * scan_path(const scan_t *scan)
get the path of a scanner
Definition: scan.c:56
CT_NODISCARD size_t size
Definition: scan.h:128
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
Definition: os.c:56
int main(int argc, const char **argv)
Definition: main.cpp:1079
EditorStyle
Definition: main.cpp:975
@ eStyleLight
Definition: main.cpp:977
@ eStyleClassic
Definition: main.cpp:978
@ eStyleDark
Definition: main.cpp:976
void draw_menu_items(ed::menu_section_t &section)
Definition: main.cpp:286
CT_BACKTRACE_API void bt_update(void)
update the loaded module cache
Definition: dbghelp.c:147
CT_BROKER_API broker_t * broker_new(const frontend_t *frontend, arena_t *arena)
Definition: broker.c:130
#define CT_NEW_VERSION(major, minor, patch)
creates a new ctu_version_t from major, minor and patch
Definition: version_def.h:20
CT_MEMORY_API void init_gmp_arena(arena_t *arena)
initialize gmp with a custom allocator
Definition: memory.c:52
CT_MEMORY_API void init_global_arena(arena_t *arena)
initialize the global memory arena
Definition: memory.c:21
CT_MEMORY_API arena_t * get_global_arena(void)
get the global memory arena
Definition: memory.c:16
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
Definition: file.c:125
CT_NODISCARD CT_IO_API os_error_t io_error(const io_t *io)
get the last error from the io object
Definition: io.c:144
CT_NODISCARD CT_IO_API size_t io_size(io_t *io)
get the total size of an io objects contents
Definition: io.c:94
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.
Definition: io.c:120
json_kind_t
the kind of json value
Definition: json.h:34
CT_SUPPORT_API void support_load_default_modules(support_t *support)
load all default modules
Definition: support.c:139
CT_SUPPORT_API bool support_load_module(support_t *support, module_type_t mask, const char *name, loaded_module_t *out)
Definition: support.c:153
CT_SUPPORT_API typevec_t * support_get_modules(support_t *support)
Definition: support.c:168
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...
Definition: support.c:113
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
Definition: node.c:57
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
Definition: node.c:65
#define CT_UNUSED(x)
mark a variable as unused
Definition: macros.h:46
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
Definition: arena.h:30
#define ARENA_OPT_MALLOC(size, name, parent, arena)
Definition: arena.h:393
#define CT_ALLOC_SIZE_UNKNOWN
unknown allocation size constant when freeing or reallocating memory, this can be used as the size to...
Definition: arena.h:37
CT_NODISCARD CT_NOTIFY_API logger_t * logger_new(arena_t *arena)
create a new logger
Definition: notify.c:21
CT_NODISCARD CT_NOTIFY_API typevec_t * logger_get_events(const logger_t *logs)
get the events from the logger
Definition: notify.c:36
CT_SETUP_API void setup_default(arena_t *arena)
initialise the runtime with default options
Definition: setup.c:188
CT_NODISCARD CT_PUREFN CT_STD_API size_t typevec_len(const typevec_t *vec)
get the length of a vector
Definition: vector.c:120
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
Definition: vector.c:191
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
CT_SUPPORT_API loader_t * loader_new(arena_t *arena)
Definition: loader.c:41
module_type_t
Definition: loader.h:32
CT_SUPPORT_API const char * load_error_string(load_error_t error)
Definition: loader.c:131
bool create(const config_t &config)
Definition: draw.cpp:310
void end_frame()
Definition: draw.cpp:433
void close()
Definition: draw.cpp:428
bool begin_frame()
Definition: draw.cpp:404
void destroy()
Definition: draw.cpp:389
bool draw_collapsing(IEditorPanel &panel, const char *title=nullptr, ImGuiTreeNodeFlags flags=ImGuiTreeNodeFlags_None)
Definition: panel.cpp:49
Compiler(TraceArena &trace, std::string_view title)
Definition: main.cpp:302
loader_t * loader
Definition: main.cpp:298
bool visible
Definition: main.cpp:314
broker_t * broker
Definition: main.cpp:299
void draw_window()
Definition: main.cpp:316
support_t * support
Definition: main.cpp:300
TraceArena & arena
Definition: main.cpp:297
std::string name
Definition: main.cpp:296
logger_t * logger
Definition: main.cpp:781
void add(const fs::path &path)
Definition: main.cpp:883
ctu::json::JsonParser parser
Definition: main.cpp:771
ctu::json::Json result
Definition: main.cpp:779
std::vector< JsonFile > documents
Definition: main.cpp:773
void draw_query_window()
Definition: main.cpp:788
TraceArena arena
Definition: main.cpp:770
void draw_json_window()
Definition: main.cpp:820
std::string query
Definition: main.cpp:777
JsonFile * document
Definition: main.cpp:778
bool visible
Definition: main.cpp:772
JsonEditor()
Definition: main.cpp:784
void draw_window()
Definition: main.cpp:872
void draw_content()
Definition: main.cpp:737
JsonFile(const fs::path &fp, ctu::json::JsonParser &parser, arena_t *arena)
Definition: main.cpp:712
ctu::json::Json value
Definition: main.cpp:710
std::string basename
Definition: main.cpp:704
std::string_view source
Definition: main.cpp:708
io_t * io
Definition: main.cpp:706
std::string path
Definition: main.cpp:703
void draw_json_document()
Definition: main.cpp:732
ctu::OsError error
Definition: main.cpp:707
void draw_source()
Definition: main.cpp:727
an allocator object
Definition: arena.h:86
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...
Definition: diagnostic.h:34
const wchar_t * title
Definition: draw.hpp:7
std::vector< ed::IEditorPanel * > panels
Definition: panel.hpp:63
std::vector< ed::IEditorPanel * > header
Definition: panel.hpp:69
std::vector< menu_section_t > sections
Definition: panel.hpp:70
an event handle TODO: make this and segment_t opaque
Definition: notify.h:36
node_t node
the primary node that this event is attached to
Definition: notify.h:41
const diagnostic_t * diagnostic
the related diagnostic
Definition: notify.h:38
vector_t * notes
extra notes that this event is attached to
Definition: notify.h:52
STA_FIELD_STRING char * message
the primary message
Definition: notify.h:44
the frontend running the mediator
Definition: broker.h:251
module_info_t info
information about the frontend
Definition: broker.h:253
io object implementation
Definition: impl.h:122
load_error_t error
Definition: loader.h:55
const plugin_t * plugin
Definition: loader.h:52
module_type_t type
Definition: loader.h:49
const target_t * target
Definition: loader.h:53
os_error_t os
Definition: loader.h:56
const language_t * lang
Definition: loader.h:51
Definition: common.h:8
a logging sink
Definition: notify.c:14
const char * id
unique id for the module
Definition: broker.h:101
a source file scanner
Definition: scan.h:24
A vector with a fixed type size.
Definition: vector.h:24
a location inside a scanner locations are inclusive and 0-based
Definition: where.h:23
ctu_column_t first_column
the first column of the location
Definition: where.h:31
ctu_line_t first_line
the first line of the location
Definition: where.h:25
CT_JSON_API json_t * json_query(json_t *json, const char *query, logger_t *logger, arena_t *arena)
query a json object
Definition: query.c:109
#define PRI_LINE
format specifier for ctu_line_t
Definition: where.h:15
#define PRI_COLUMN
format specifier for ctu_column_t
Definition: where.h:18