summaryrefslogtreecommitdiff
path: root/DevTools/HackStudio
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-10-29 21:39:12 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-10-30 20:28:44 +0100
commit7c71040ba9e6adcf25cce07d718ef7b996dcfa50 (patch)
treec3b419e53f2679c642d177ee32c89d83200d5342 /DevTools/HackStudio
parenta4709502d1b9a9b2635343e8b03d89a0b89815e6 (diff)
downloadserenity-7c71040ba9e6adcf25cce07d718ef7b996dcfa50.zip
HackStudio: Show documentation preview in tooltip on identifier hover
When hovering over a C++ token that we have a man page for, we now show the man page in a tooltip window. This feels rather bulky at the moment, but the basic mechanism is quite neat and just needs a bunch of tuning.
Diffstat (limited to 'DevTools/HackStudio')
-rw-r--r--DevTools/HackStudio/Editor.cpp118
-rw-r--r--DevTools/HackStudio/Editor.h14
-rw-r--r--DevTools/HackStudio/Makefile2
3 files changed, 127 insertions, 7 deletions
diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp
index fe0666d48f..1ea850b781 100644
--- a/DevTools/HackStudio/Editor.cpp
+++ b/DevTools/HackStudio/Editor.cpp
@@ -1,7 +1,33 @@
#include "Editor.h"
#include "EditorWrapper.h"
+#include <AK/FileSystemPath.h>
+#include <LibCore/CDirIterator.h>
+#include <LibCore/CFile.h>
+#include <LibGUI/GApplication.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GScrollBar.h>
+#include <LibGUI/GWindow.h>
+#include <LibHTML/DOM/ElementFactory.h>
+#include <LibHTML/DOM/HTMLHeadElement.h>
+#include <LibHTML/DOM/Text.h>
+#include <LibHTML/HtmlView.h>
+#include <LibHTML/Parser/HTMLParser.h>
+#include <LibMarkdown/MDDocument.h>
+
+Editor::Editor(GWidget* parent)
+ : GTextEditor(GTextEditor::MultiLine, parent)
+{
+ m_documentation_tooltip_window = GWindow::construct();
+ m_documentation_tooltip_window->set_rect(0, 0, 500, 400);
+ m_documentation_tooltip_window->set_window_type(GWindowType::Tooltip);
+
+ m_documentation_html_view = HtmlView::construct(nullptr);
+ m_documentation_tooltip_window->set_main_widget(m_documentation_html_view);
+}
+
+Editor::~Editor()
+{
+}
EditorWrapper& Editor::wrapper()
{
@@ -20,7 +46,7 @@ void Editor::focusin_event(CEvent& event)
GTextEditor::focusin_event(event);
}
-void Editor::focusout_event(CEvent & event)
+void Editor::focusout_event(CEvent& event)
{
wrapper().set_editor_has_focus({}, false);
GTextEditor::focusout_event(event);
@@ -42,3 +68,93 @@ void Editor::paint_event(GPaintEvent& event)
painter.draw_rect(rect, Color::from_rgb(0x955233));
}
}
+
+static HashMap<String, String>& man_paths()
+{
+ static HashMap<String, String> paths;
+ if (paths.is_empty()) {
+ // FIXME: This should also search man3, possibly other places..
+ CDirIterator it("/usr/share/man/man2", CDirIterator::Flags::SkipDots);
+ while (it.has_next()) {
+ auto path = String::format("/usr/share/man/man2/%s", it.next_path().characters());
+ auto title = FileSystemPath(path).title();
+ paths.set(title, path);
+ }
+ }
+
+ return paths;
+}
+
+void Editor::show_documentation_tooltip_if_available(const String& hovered_token, const Point& screen_location)
+{
+ auto it = man_paths().find(hovered_token);
+ if (it == man_paths().end()) {
+ dbg() << "no man path for " << hovered_token;
+ m_documentation_tooltip_window->hide();
+ return;
+ }
+
+ dbg() << "opening " << it->value;
+ auto file = CFile::construct(it->value);
+ if (!file->open(CFile::ReadOnly)) {
+ dbg() << "failed to open " << it->value << " " << file->error_string();
+ return;
+ }
+
+ MDDocument man_document;
+ bool success = man_document.parse(file->read_all());
+
+ if (!success) {
+ dbg() << "failed to parse markdown";
+ return;
+ }
+
+ auto html_text = man_document.render_to_html();
+
+ auto html_document = parse_html(html_text);
+ if (!html_document) {
+ dbg() << "failed to parse HTML";
+ return;
+ }
+
+ // FIXME: LibHTML needs a friendlier DOM manipulation API. Something like innerHTML :^)
+ auto style_element = create_element(*html_document, "style");
+ style_element->append_child(adopt(*new Text(*html_document, "body { background-color: #dac7b5; }")));
+
+ // FIXME: This const_cast should not be necessary.
+ auto* head_element = const_cast<HTMLHeadElement*>(html_document->head());
+ ASSERT(head_element);
+ head_element->append_child(style_element);
+
+ m_documentation_html_view->set_document(html_document);
+ m_documentation_tooltip_window->move_to(screen_location.translated(4, 4));
+ m_documentation_tooltip_window->show();
+}
+
+void Editor::mousemove_event(GMouseEvent& event)
+{
+ GTextEditor::mousemove_event(event);
+
+ if (document().spans().is_empty())
+ return;
+
+ auto text_position = text_position_at(event.position());
+ if (!text_position.is_valid()) {
+ GApplication::the().hide_tooltip();
+ return;
+ }
+
+ for (auto& span : document().spans()) {
+ if (span.range.contains(text_position)) {
+ auto adjusted_range = span.range;
+ adjusted_range.end().set_column(adjusted_range.end().column() + 1);
+ auto hovered_span_text = document().text_in_range(adjusted_range);
+#ifdef EDITOR_DEBUG
+ dbg() << "Hovering: " << adjusted_range << " \"" << hovered_span_text << "\"";
+#endif
+ show_documentation_tooltip_if_available(hovered_span_text, event.position().translated(screen_relative_rect().location()));
+ return;
+ }
+ }
+ GApplication::the().hide_tooltip();
+}
diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h
index 92858b9e69..9d4a9e93ff 100644
--- a/DevTools/HackStudio/Editor.h
+++ b/DevTools/HackStudio/Editor.h
@@ -3,11 +3,12 @@
#include <LibGUI/GTextEditor.h>
class EditorWrapper;
+class HtmlView;
class Editor final : public GTextEditor {
C_OBJECT(Editor)
public:
- virtual ~Editor() override {}
+ virtual ~Editor() override;
Function<void()> on_focus;
@@ -18,9 +19,12 @@ private:
virtual void focusin_event(CEvent&) override;
virtual void focusout_event(CEvent&) override;
virtual void paint_event(GPaintEvent&) override;
+ virtual void mousemove_event(GMouseEvent&) override;
- Editor(GWidget* parent)
- : GTextEditor(GTextEditor::MultiLine, parent)
- {
- }
+ void show_documentation_tooltip_if_available(const String&, const Point& screen_location);
+
+ explicit Editor(GWidget* parent);
+
+ RefPtr<GWindow> m_documentation_tooltip_window;
+ RefPtr<HtmlView> m_documentation_html_view;
};
diff --git a/DevTools/HackStudio/Makefile b/DevTools/HackStudio/Makefile
index c92fd15e1b..8d874da8cf 100644
--- a/DevTools/HackStudio/Makefile
+++ b/DevTools/HackStudio/Makefile
@@ -19,7 +19,7 @@ DEFINES += -DUSERLAND
all: $(APP)
$(APP): $(OBJS)
- $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lvt -lgui -ldraw -lthread -lcore -lc
+ $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lvt -lhtml -lmarkdown -lgui -ldraw -lthread -lcore -lc
.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<