mirror of https://gitlab.com/cppit/jucipp
9 changed files with 224 additions and 2 deletions
@ -0,0 +1,141 @@ |
|||||||
|
#include "source_coverage.hpp" |
||||||
|
#include <algorithm> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
Source::CoverageView::Renderer::Renderer() : Gsv::GutterRendererText() { |
||||||
|
set_padding(4, 0); |
||||||
|
} |
||||||
|
|
||||||
|
Source::CoverageView::CoverageView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language) : BaseView(file_path, language), renderer(new Renderer()) {} |
||||||
|
|
||||||
|
Source::CoverageView::~CoverageView() { |
||||||
|
get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT)->remove(renderer.get()); |
||||||
|
} |
||||||
|
|
||||||
|
void Source::CoverageView::configure() { |
||||||
|
// Set colors
|
||||||
|
auto &yellow = renderer->yellow; |
||||||
|
auto &red = renderer->red; |
||||||
|
auto &green = renderer->green; |
||||||
|
auto &transparent = renderer->transparent; |
||||||
|
auto normal_color = get_style_context()->get_color(Gtk::StateFlags::STATE_FLAG_NORMAL); |
||||||
|
auto light_theme = (normal_color.get_red() + normal_color.get_green() + normal_color.get_blue()) / 3 < 0.5; |
||||||
|
yellow.set_rgba(1.0, 1.0, 0.2); |
||||||
|
double factor = light_theme ? 0.85 : 0.5; |
||||||
|
yellow.set_red(normal_color.get_red() + factor * (yellow.get_red() - normal_color.get_red())); |
||||||
|
yellow.set_green(normal_color.get_green() + factor * (yellow.get_green() - normal_color.get_green())); |
||||||
|
yellow.set_blue(normal_color.get_blue() + factor * (yellow.get_blue() - normal_color.get_blue())); |
||||||
|
red.set_rgba(1.0, 0.0, 0.0); |
||||||
|
factor = light_theme ? 0.8 : 0.35; |
||||||
|
red.set_red(normal_color.get_red() + factor * (red.get_red() - normal_color.get_red())); |
||||||
|
red.set_green(normal_color.get_green() + factor * (red.get_green() - normal_color.get_green())); |
||||||
|
red.set_blue(normal_color.get_blue() + factor * (red.get_blue() - normal_color.get_blue())); |
||||||
|
green.set_rgba(0.0, 1.0, 0.0); |
||||||
|
factor = light_theme ? 0.7 : 0.4; |
||||||
|
green.set_red(normal_color.get_red() + factor * (green.get_red() - normal_color.get_red())); |
||||||
|
green.set_green(normal_color.get_green() + factor * (green.get_green() - normal_color.get_green())); |
||||||
|
green.set_blue(normal_color.get_blue() + factor * (green.get_blue() - normal_color.get_blue())); |
||||||
|
transparent.set_rgba(1.0, 1.0, 1.0, 1.0); |
||||||
|
|
||||||
|
// Style gutter
|
||||||
|
renderer->set_alignment_mode(Gsv::GutterRendererAlignmentMode::GUTTER_RENDERER_ALIGNMENT_MODE_FIRST); |
||||||
|
renderer->set_alignment(1.0, -1); |
||||||
|
renderer->set_padding(3, -1); |
||||||
|
|
||||||
|
// Connect gutter renderer signals
|
||||||
|
renderer->signal_query_data().connect([this](const Gtk::TextIter &start, const Gtk::TextIter &end, Gsv::GutterRendererState state) { |
||||||
|
update_gutter(find_coverage(start.get_line())); |
||||||
|
}); |
||||||
|
renderer->signal_query_activatable().connect([this](const Gtk::TextIter &, const Gdk::Rectangle &, GdkEvent *) { |
||||||
|
return !line_coverage.empty(); |
||||||
|
}); |
||||||
|
|
||||||
|
renderer->signal_query_tooltip().connect([this](const Gtk::TextIter &iter, const Gdk::Rectangle &area, int x, int y, const Glib::RefPtr<Gtk::Tooltip> &tooltip) { |
||||||
|
if(const auto *entry = find_coverage(iter.get_line())) { |
||||||
|
tooltip->set_text(update_tooltip(*entry)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}); |
||||||
|
|
||||||
|
get_gutter(Gtk::TextWindowType::TEXT_WINDOW_LEFT)->insert(renderer.get(), -50); |
||||||
|
renderer->set_visible(false); |
||||||
|
} |
||||||
|
|
||||||
|
void Source::CoverageView::toggle_coverage() { |
||||||
|
if(!line_coverage.empty()) { |
||||||
|
update_coverage({}); |
||||||
|
} |
||||||
|
else if(get_coverage) { |
||||||
|
update_coverage(get_coverage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Source::CoverageView::update_coverage(std::vector<Coverage::LineCoverage> &&coverage) { |
||||||
|
line_coverage = std::move(coverage); |
||||||
|
unsigned long max_line_count = 0; |
||||||
|
for(const auto &line : line_coverage) { |
||||||
|
max_line_count = std::max(max_line_count, line.count); |
||||||
|
} |
||||||
|
|
||||||
|
if(line_coverage.empty()) { |
||||||
|
renderer->set_visible(false); |
||||||
|
} |
||||||
|
else { |
||||||
|
int width, height; |
||||||
|
renderer->measure(std::to_string(max_line_count), width, height); |
||||||
|
renderer->set_visible(true); |
||||||
|
renderer->set_size(width); |
||||||
|
} |
||||||
|
|
||||||
|
renderer->queue_draw(); |
||||||
|
} |
||||||
|
|
||||||
|
const Coverage::LineCoverage *Source::CoverageView::find_coverage(int line) { |
||||||
|
if(line_coverage.empty()) { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
auto it = std::lower_bound(line_coverage.begin(), line_coverage.end(), line, [](const Coverage::LineCoverage &entry, int line) { |
||||||
|
return entry.line < static_cast<unsigned long>(line); |
||||||
|
}); |
||||||
|
if(it != line_coverage.end() && it->line == static_cast<unsigned long>(line)) { |
||||||
|
return &(*it); |
||||||
|
} |
||||||
|
|
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void Source::CoverageView::update_gutter(const Coverage::LineCoverage *line_coverage) { |
||||||
|
if(!line_coverage) { |
||||||
|
renderer->set_text(""); |
||||||
|
renderer->set_background(renderer->transparent); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
renderer->set_text(std::to_string(line_coverage->count)); |
||||||
|
if(line_coverage->fully_covered()) { |
||||||
|
renderer->set_background(renderer->green); |
||||||
|
} |
||||||
|
else if(line_coverage->partially_covered()) { |
||||||
|
renderer->set_background(renderer->yellow); |
||||||
|
} |
||||||
|
else { |
||||||
|
renderer->set_background(renderer->red); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string Source::CoverageView::update_tooltip(const Coverage::LineCoverage &line_coverage) { |
||||||
|
std::stringstream ss; |
||||||
|
ss << "Line executed " << line_coverage.count << " times"; |
||||||
|
if(line_coverage.count > 0 && line_coverage.has_unexecuted_statements) { |
||||||
|
ss << " (with unexecuted statements)"; |
||||||
|
} |
||||||
|
|
||||||
|
if(!line_coverage.branches.empty()) { |
||||||
|
ss << ", with branches:"; |
||||||
|
for(const auto &branch : line_coverage.branches) { |
||||||
|
ss << "\n- " << (branch.is_fallthrough ? "Fall-through " : "") << (branch.is_throw ? "Exceptional " : "") << "Branch executed " << branch.count << " times"; |
||||||
|
} |
||||||
|
} |
||||||
|
return ss.str(); |
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
#pragma once |
||||||
|
#include "coverage.hpp" |
||||||
|
#include "source_base.hpp" |
||||||
|
|
||||||
|
namespace Source { |
||||||
|
class CoverageView : virtual public Source::BaseView { |
||||||
|
|
||||||
|
class Renderer : public Gsv::GutterRendererText { |
||||||
|
public: |
||||||
|
Renderer(); |
||||||
|
|
||||||
|
Gdk::RGBA yellow, red, green, transparent; |
||||||
|
}; |
||||||
|
|
||||||
|
public: |
||||||
|
CoverageView(const boost::filesystem::path &file_path, const Glib::RefPtr<Gsv::Language> &language); |
||||||
|
~CoverageView() override; |
||||||
|
|
||||||
|
void configure() override; |
||||||
|
|
||||||
|
std::function<std::vector<Coverage::LineCoverage>()> get_coverage; |
||||||
|
void toggle_coverage(); |
||||||
|
|
||||||
|
private: |
||||||
|
std::unique_ptr<Renderer> renderer; |
||||||
|
std::vector<Coverage::LineCoverage> line_coverage; |
||||||
|
|
||||||
|
void update_coverage(std::vector<Coverage::LineCoverage> &&coverage); |
||||||
|
void update_gutter(const Coverage::LineCoverage *line_coverage); |
||||||
|
std::string update_tooltip(const Coverage::LineCoverage &line_coverage); |
||||||
|
|
||||||
|
const Coverage::LineCoverage *find_coverage(int line); |
||||||
|
}; |
||||||
|
} // namespace Source
|
||||||
Loading…
Reference in new issue