feat(files): add extension badges for common file types

This commit is contained in:
2026-06-18 23:25:49 -05:00
parent 508c0f037e
commit 0425bb7320

View File

@@ -1626,6 +1626,57 @@ ImVec4 change_color(FileChangeKind kind) {
return {0.58f, 0.61f, 0.67f, 1};
}
struct FileTypeBadge {
const char* label = nullptr;
ImU32 background = IM_COL32(66, 72, 82, 255);
ImU32 text = IM_COL32(226, 229, 233, 255);
};
FileTypeBadge file_type_badge(std::string_view path) {
const auto extension = std::filesystem::path(path).extension().string();
std::string lowered = extension;
std::transform(lowered.begin(), lowered.end(), lowered.begin(), [](unsigned char value) {
return static_cast<char>(std::tolower(value));
});
if (lowered == ".c") return {"C", IM_COL32(58, 91, 168, 255)};
if (lowered == ".h") return {"H", IM_COL32(72, 93, 138, 255)};
if (lowered == ".cc" || lowered == ".cpp" || lowered == ".cxx" ||
lowered == ".hh" || lowered == ".hpp" || lowered == ".hxx")
return {"C++", IM_COL32(53, 108, 186, 255)};
if (lowered == ".cs") return {"C#", IM_COL32(115, 82, 180, 255)};
if (lowered == ".rs") return {"RS", IM_COL32(170, 96, 55, 255)};
if (lowered == ".py" || lowered == ".pyw") return {"PY", IM_COL32(76, 122, 190, 255)};
if (lowered == ".js" || lowered == ".jsx" || lowered == ".mjs" || lowered == ".cjs")
return {"JS", IM_COL32(192, 164, 50, 255), IM_COL32(30, 30, 34, 255)};
if (lowered == ".ts" || lowered == ".tsx") return {"TS", IM_COL32(45, 119, 204, 255)};
if (lowered == ".lua") return {"LUA", IM_COL32(80, 97, 184, 255)};
if (lowered == ".sh" || lowered == ".bash" || lowered == ".zsh") return {"SH", IM_COL32(74, 124, 88, 255)};
if (lowered == ".bat" || lowered == ".cmd") return {"BAT", IM_COL32(102, 102, 112, 255)};
if (lowered == ".ps1") return {"PS", IM_COL32(57, 108, 173, 255)};
if (lowered == ".json") return {"{}", IM_COL32(128, 110, 62, 255)};
if (lowered == ".xml" || lowered == ".html" || lowered == ".htm") return {"<>", IM_COL32(160, 88, 62, 255)};
if (lowered == ".css" || lowered == ".scss") return {"CSS", IM_COL32(88, 112, 201, 255)};
if (lowered == ".md") return {"MD", IM_COL32(84, 97, 112, 255)};
if (lowered == ".yml" || lowered == ".yaml") return {"YML", IM_COL32(132, 74, 74, 255)};
if (lowered == ".toml") return {"TOML", IM_COL32(126, 88, 70, 255)};
return {};
}
float file_type_badge_width(const FileTypeBadge& badge) {
return badge.label ? ImGui::CalcTextSize(badge.label).x + ui(10.0f) : 0.0f;
}
void draw_file_type_badge(ImDrawList* draw, const ImVec2& minimum, float y, const FileTypeBadge& badge) {
if (!badge.label) return;
const float width = file_type_badge_width(badge);
const float height = ui(16.0f);
const ImVec2 top_left{minimum.x + ui(19.0f), minimum.y + (ui(25.0f) - height) * 0.5f};
const ImVec2 bottom_right{top_left.x + width, top_left.y + height};
draw->AddRectFilled(top_left, bottom_right, badge.background, ui(3.0f));
const ImVec2 text_size = ImGui::CalcTextSize(badge.label);
draw->AddText({top_left.x + (width - text_size.x) * 0.5f, y}, badge.text, badge.label);
}
float file_action_button_width(const char* label) {
return ImGui::CalcTextSize(label).x + ui(16.0f);
}
@@ -1674,8 +1725,11 @@ void draw_file_row(const std::string& path, FileChangeKind kind, int id,
ImDrawList* draw = ImGui::GetWindowDrawList();
if (row_hovered) draw->AddRectFilled(minimum, maximum, IM_COL32(48, 52, 60, 255));
const float y = minimum.y + (maximum.y - minimum.y - ImGui::GetFontSize()) * 0.5f;
const FileTypeBadge badge = file_type_badge(git_path);
draw->AddText({minimum.x + ui(4.0f), y}, ImGui::ColorConvertFloat4ToU32(change_color(kind)), change_icon(kind));
draw->AddText({minimum.x + ui(20.0f), y}, IM_COL32(205, 209, 216, 255), path.c_str());
draw_file_type_badge(draw, minimum, y, badge);
const float text_x = minimum.x + ui(20.0f) + (badge.label ? file_type_badge_width(badge) + ui(6.0f) : 0.0f);
draw->AddText({text_x, y}, IM_COL32(205, 209, 216, 255), path.c_str());
if (ImGui::BeginPopupContextItem()) {
if (working_file && !staged && ImGui::MenuItem(ICON_TB_PLUS " Stage file"))
g_git_manager->stageFile(repo(), git_path, g_notice);