From e95c89ccbee4184e5d5853f8297b6548c955933f Mon Sep 17 00:00:00 2001 From: eidheim Date: Tue, 30 Jun 2015 22:43:20 +0200 Subject: [PATCH] Added diagnostics; warning and error messages while writing code. --- juci/CMakeLists.txt | 2 ++ juci/notebook.cc | 1 + juci/source.cc | 51 +++++++++++++++++++++++++++++++- juci/source.h | 5 ++++ juci/tooltips.cc | 71 +++++++++++++++++++++++++++++++++++++++++++++ juci/tooltips.h | 42 +++++++++++++++++++++++++++ juci/window.cc | 1 + 7 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 juci/tooltips.cc create mode 100644 juci/tooltips.h diff --git a/juci/CMakeLists.txt b/juci/CMakeLists.txt index b7a518e..0fae989 100644 --- a/juci/CMakeLists.txt +++ b/juci/CMakeLists.txt @@ -125,6 +125,8 @@ add_executable(${project_name} directories.cc terminal.h terminal.cc + tooltips.h + tooltips.cc ) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) diff --git a/juci/notebook.cc b/juci/notebook.cc index fa52666..6c0d90f 100644 --- a/juci/notebook.cc +++ b/juci/notebook.cc @@ -187,6 +187,7 @@ void Notebook::Controller::OnOpenFile(std::string path) { Notebook().set_current_page(Pages()-1); Notebook().set_focus_child(*text_vec_.back()->view); //Add star on tab label when the page is not saved: + //TODO: instead use Gtk::TextBuffer::set_modified and Gtk::TextBuffer::get_modified text_vec_.back()->buffer()->signal_changed().connect([this]() { if(text_vec_.at(CurrentPage())->is_saved) { std::string path=CurrentTextView().file_path; diff --git a/juci/source.cc b/juci/source.cc index 1275cca..13484d0 100644 --- a/juci/source.cc +++ b/juci/source.cc @@ -136,7 +136,7 @@ clang::Index Source::ClangView::clang_index(0, 0); Source::ClangView::ClangView(const Source::Config& config, const std::string& file_path, const std::string& project_path, Terminal::Controller& terminal): Source::View(config, file_path, project_path), terminal(terminal), -parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { +parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false), diagnostic_tooltips(*this) { override_font(Pango::FontDescription(config.font)); override_background_color(Gdk::RGBA(config.background)); for (auto &item : config.tags) { @@ -183,6 +183,7 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { update_syntax(extract_tokens(0, get_source_buffer()->get_text().size())); parsing_in_progress->done("done"); INFO("Syntax updated"); + update_diagnostics(); } else { parse_thread_go=true; @@ -216,6 +217,8 @@ parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { signal_key_press_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_key_press), false); signal_key_release_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_key_release), false); + signal_motion_notify_event().connect(sigc::mem_fun(*this, &Source::ClangView::on_motion_notify_event), false); + get_buffer()->signal_mark_set().connect(sigc::mem_fun(*this, &Source::ClangView::on_mark_set), false); } Source::ClangView::~ClangView() { @@ -346,6 +349,52 @@ void Source::ClangView::update_syntax(const std::vector &ranges) } } +void Source::ClangView::update_diagnostics() { + diagnostic_tooltips.clear(); + auto diagnostics=tu_->get_diagnostics(); + auto buffer=get_source_buffer(); + for(auto& diagnostic: diagnostics) { + if(diagnostic.path==file_path) { + auto start=buffer->get_iter_at_offset(diagnostic.start_location.offset); + auto end=buffer->get_iter_at_offset(diagnostic.end_location.offset); + diagnostic_tooltips.add(diagnostic.severity_spelling+": "+diagnostic.spelling, get_source_buffer()->create_mark(start), get_source_buffer()->create_mark(end)); + auto tag=buffer->create_tag(); + tag->property_underline()=Pango::Underline::UNDERLINE_ERROR; + if(diagnostic.severity<=CXDiagnostic_Warning) { + //TODO: get color from config.json + tag->set_property("underline-rgba", Gdk::RGBA("orange")); + } + else { + //TODO: get color from config.json + tag->set_property("underline-rgba", Gdk::RGBA("red")); + } + buffer->apply_tag(tag, start, end); + } + } + on_mark_set(get_buffer()->get_insert()->get_iter(), get_buffer()->get_mark("insert")); +} + +bool Source::ClangView::on_motion_notify_event(GdkEventMotion* event) { + Gdk::Rectangle rectangle(event->x, event->y, 1, 1); + diagnostic_tooltips.show(rectangle); + auto cursor=Gdk::Cursor::create(Gdk::CursorType::XTERM); + get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->set_cursor(cursor); + return false; +} + +void Source::ClangView::on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark) { + if(mark->get_name()=="insert") { + Gdk::Rectangle rectangle; + get_iter_location(iterator, rectangle); + int location_window_x, location_window_y; + buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, rectangle.get_x(), rectangle.get_y(), location_window_x, location_window_y); + rectangle.set_x(location_window_x-2); + rectangle.set_y(location_window_y); + rectangle.set_width(4); + diagnostic_tooltips.show(rectangle); + } +} + void Source::ClangView:: highlight_cursor(clang::Token *token, std::vector *source_ranges) { diff --git a/juci/source.h b/juci/source.h index d6ec5b1..492717e 100644 --- a/juci/source.h +++ b/juci/source.h @@ -11,6 +11,7 @@ #include #include "gtksourceviewmm.h" #include "terminal.h" +#include "tooltips.h" namespace Source { class Config { @@ -95,6 +96,10 @@ namespace Source { int reparse(const std::map &buffers); std::vector extract_tokens(int, int); void update_syntax(const std::vector &locations); + void update_diagnostics(); + Tooltips diagnostic_tooltips; + bool on_motion_notify_event(GdkEventMotion* event); + void on_mark_set(const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark); static clang::Index clang_index; std::map get_buffer_map() const; diff --git a/juci/tooltips.cc b/juci/tooltips.cc new file mode 100644 index 0000000..9f9ca50 --- /dev/null +++ b/juci/tooltips.cc @@ -0,0 +1,71 @@ +#include "tooltips.h" +#include + +Tooltip::Tooltip(Gtk::TextView& text_view, const std::string& label_text, +Glib::RefPtr start_mark, Glib::RefPtr end_mark, Gdk::Rectangle &tooltips_rectangle): +text_view(text_view), Gtk::Dialog("", (Gtk::Window&)*text_view.get_toplevel()), label(label_text), +start_mark(start_mark), end_mark(end_mark), tooltips_rectangle(tooltips_rectangle) { + get_content_area()->add(label); + property_decorated()=false; + set_accept_focus(false); +} + +void Tooltip::update() { + auto iter=start_mark->get_iter(); + auto end_iter=end_mark->get_iter(); + if(iter.get_offset()get_root_coords(text_rectangle.get_x(), text_rectangle.get_y(), root_x, root_y); + Gdk::Rectangle rectangle; + rectangle.set_x(root_x); + rectangle.set_y(root_y-tooltip_height); + rectangle.set_width(tooltip_width); + rectangle.set_height(tooltip_height); + + if(tooltips_rectangle.get_width()!=0) { + if(rectangle.intersects(tooltips_rectangle)) + rectangle.set_y(tooltips_rectangle.get_y()-tooltip_height); + tooltips_rectangle.join(rectangle); + } + else + tooltips_rectangle=rectangle; + + if(rectangle.get_y()<0) + rectangle.set_y(0); + move(rectangle.get_x(), rectangle.get_y()); +} + +void Tooltips::add(const std::string& text, Glib::RefPtr start_mark, Glib::RefPtr end_mark) { + tooltips.emplace_back(new Tooltip(text_view, text, start_mark, end_mark, tooltips_rectangle)); +} + +void Tooltips::show(const Gdk::Rectangle& rectangle) { + init_adjustments(); + for(auto& tooltip: tooltips) { + tooltip->update(); + if(rectangle.intersects(tooltip->text_rectangle)) { + tooltip->show_all(); + tooltip->adjust(); + } + else + tooltip->hide(); + } +} \ No newline at end of file diff --git a/juci/tooltips.h b/juci/tooltips.h new file mode 100644 index 0000000..9ddf4c8 --- /dev/null +++ b/juci/tooltips.h @@ -0,0 +1,42 @@ +#ifndef JUCI_TOOLTIPS_H_ +#define JUCI_TOOLTIPS_H_ +#include "gtkmm.h" +#include +#include + +class Tooltip : public Gtk::Dialog { +public: + Tooltip(Gtk::TextView& text_view, const std::string& label_text, Glib::RefPtr start_mark, Glib::RefPtr end_mark, Gdk::Rectangle &tooltips_rectangle); + + void update(); + void adjust(); + + Gtk::Label label; + Glib::RefPtr start_mark; + Glib::RefPtr end_mark; + Gdk::Rectangle text_rectangle; + +private: + Gtk::TextView& text_view; + Gdk::Rectangle &tooltips_rectangle; +}; + +class Tooltips { +public: + Tooltips(Gtk::TextView& text_view): text_view(text_view) {} + + void init_adjustments() {tooltips_rectangle=Gdk::Rectangle();} + + void clear() {tooltips.clear();} + + void add(const std::string& text, Glib::RefPtr start_mark, Glib::RefPtr end_mark); + + void show(const Gdk::Rectangle& rectangle); + + Gdk::Rectangle tooltips_rectangle; +private: + Gtk::TextView& text_view; + std::vector > tooltips; +}; + +#endif // JUCI_TOOLTIPS_H_ \ No newline at end of file diff --git a/juci/window.cc b/juci/window.cc index 6c54878..d784d23 100644 --- a/juci/window.cc +++ b/juci/window.cc @@ -14,6 +14,7 @@ Window::Window() : INFO("Create Window"); set_title("juCi++"); set_default_size(600, 400); + set_events(Gdk::POINTER_MOTION_MASK); add(window_box_); keybindings_.action_group_menu()->add(Gtk::Action::create("FileQuit", "Quit juCi++"),