feat(blame): restore rich code view and minimap

This commit is contained in:
2026-06-18 23:45:30 -05:00
parent 5a2cffc177
commit 0b220a382e

View File

@@ -442,8 +442,8 @@ void drawMinimap(const std::vector<MinimapEntry>& entries, float scale,
void drawCodeLine(const std::string& text, SyntaxLanguage language, SyntaxState& syntax,
float scale, ImU32 background = IM_COL32(0, 0, 0, 0), float left_gutter = 0.0f,
float minimum_width = 0.0f) {
const float row_height = scaled(21.0f, scale);
float minimum_width = 0.0f, float custom_row_height = 0.0f) {
const float row_height = custom_row_height > 0.0f ? custom_row_height : scaled(21.0f, scale);
const ImVec2 start = ImGui::GetCursorScreenPos();
const float width = std::max(minimum_width, ImGui::CalcTextSize(text.c_str()).x + left_gutter + scaled(12.0f, scale));
ImGui::InvisibleButton("##code_line", {width, row_height});
@@ -659,7 +659,6 @@ void DiffViewer::loadSupplement(RepositoryView& repository, GitManager& manager,
void DiffViewer::draw(RepositoryView& repository, GitManager& manager, AvatarCache* avatars,
float scale, ImFont* code_font, std::string& notice) {
(void)avatars;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {scaled(8, scale), scaled(5, scale)});
ImGui::BeginChild("diff_viewer", {-1, -1}, ImGuiChildFlags_None,
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
@@ -713,7 +712,7 @@ void DiffViewer::draw(RepositoryView& repository, GitManager& manager, AvatarCac
ImGui::Separator();
}
const bool show_minimap = mode_ == Mode::diff || mode_ == Mode::file;
const bool show_minimap = mode_ == Mode::diff || mode_ == Mode::file || mode_ == Mode::blame;
const float minimap_width = scaled(56.0f, scale);
float main_scroll_y = 0.0f;
float main_window_height = 0.0f;
@@ -736,10 +735,14 @@ void DiffViewer::draw(RepositoryView& repository, GitManager& manager, AvatarCac
}
minimap_entries.push_back({0, IM_COL32(0, 0, 0, 0)});
}
} else {
} else if (mode_ == Mode::file) {
minimap_entries.reserve(file_lines_.size());
for (const std::string& line : file_lines_)
minimap_entries.push_back({line.size(), IM_COL32(112, 118, 128, 255)});
} else {
minimap_entries.reserve(blame_lines_.size());
for (const BlameLine& line : blame_lines_)
minimap_entries.push_back({line.text.size(), blameColor(line.hash, line.show_attribution ? 200 : 125)});
}
}
@@ -820,22 +823,55 @@ void DiffViewer::draw(RepositoryView& repository, GitManager& manager, AvatarCac
reload(repository, manager, notice);
}
} else if (mode_ == Mode::blame) {
std::ostringstream blame_text;
for (size_t index = 0; index < blame_lines_.size(); ++index) {
const BlameLine& line = blame_lines_[index];
if (index) blame_text << '\n';
blame_text << line.line_number << " ";
if (line.show_attribution) {
blame_text << line.summary;
if (!line.date.empty()) blame_text << " " << line.date;
blame_text << " ";
} else {
blame_text << "| ";
if (blame_lines_.empty()) {
ImGui::TextDisabled("No blame data is available for this file.");
} else {
SyntaxState syntax;
const float info_width = scaled(270.0f, scale);
const float line_number_width = scaled(48.0f, scale);
const float code_gutter = info_width + line_number_width + scaled(14.0f, scale);
for (size_t index = 0; index < blame_lines_.size(); ++index) {
ImGui::PushID(static_cast<int>(index));
const BlameLine& line = blame_lines_[index];
const ImVec2 line_minimum = ImGui::GetCursorScreenPos();
const ImU32 accent = blameColor(line.hash, line.show_attribution ? 255 : 190);
const ImU32 background = line.show_attribution ? IM_COL32(39, 42, 50, 150) : IM_COL32(31, 34, 40, 105);
const float blame_row_height = line.show_attribution ? scaled(28.0f, scale) : scaled(21.0f, scale);
drawCodeLine(line.text, language, syntax, scale, background, code_gutter, content_width,
blame_row_height);
ImDrawList* draw = ImGui::GetWindowDrawList();
const ImVec2 line_maximum = ImGui::GetItemRectMax();
draw->AddRectFilled({line_minimum.x, line_minimum.y},
{line_minimum.x + scaled(3.0f, scale), line_maximum.y}, accent);
drawCodeLineNumber(line.line_number, line_minimum.x + info_width + scaled(6.0f, scale),
line_minimum.y + scaled(2.0f, scale), IM_COL32(126, 132, 142, 255));
if (line.show_attribution) {
const float avatar_size = scaled(16.0f, scale);
const ImVec2 avatar_min{line_minimum.x + scaled(8.0f, scale), line_minimum.y + scaled(2.0f, scale)};
const unsigned int avatar_texture = avatars ? avatars->textureFor(line.email) : 0;
if (avatar_texture) {
draw->AddImageRounded(ImTextureRef(static_cast<ImTextureID>(avatar_texture)),
avatar_min, {avatar_min.x + avatar_size, avatar_min.y + avatar_size},
{0, 0}, {1, 1}, IM_COL32_WHITE, scaled(3.0f, scale));
} else {
draw->AddRectFilled(avatar_min, {avatar_min.x + avatar_size, avatar_min.y + avatar_size},
accent, scaled(3.0f, scale));
}
const float info_x = avatar_min.x + avatar_size + scaled(8.0f, scale);
const std::string author = line.author.empty() ? "Unknown author" : line.author;
std::string summary = line.summary.empty() ? "(no summary)" : line.summary;
if (!line.date.empty()) summary += " " + line.date;
draw->AddText({info_x, line_minimum.y + scaled(1.0f, scale)},
IM_COL32(216, 220, 226, 255), author.c_str());
draw->AddText({info_x, line_minimum.y + scaled(10.0f, scale)},
IM_COL32(134, 140, 151, 255), summary.c_str());
} else {
draw->AddText({line_minimum.x + scaled(16.0f, scale), line_minimum.y + scaled(2.0f, scale)},
IM_COL32(92, 99, 110, 255), "...");
}
ImGui::PopID();
}
blame_text << line.text;
}
drawSelectableTextBlock("##blame_text", blame_text.str(), {-1, -1});
if (blame_lines_.empty()) ImGui::TextDisabled("No blame data is available for this file.");
} else {
const std::vector<std::string>* lines = mode_ == Mode::file ? &file_lines_ : &history_lines_;
if (mode_ == Mode::file) {