From 04823e1a7af0998e56a5a64154a10478eb5a2028 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sun, 1 Mar 2026 10:59:13 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Convert=20plugin=20to=20c+?= =?UTF-8?q?+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 9 + CMakeLists.txt | 9 + README.md | 60 +++++ include/albert/plugin/gitmoji.h | 15 ++ metadata.json | 7 + src/configwidget.ui | 55 ++++ src/gitmojis.json | 437 ++++++++++++++++++++++++++++++++ src/gitmojis.qrc | 5 + src/plugin.cpp | 169 ++++++++++++ src/plugin.h | 25 ++ 10 files changed, 791 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 include/albert/plugin/gitmoji.h create mode 100644 metadata.json create mode 100644 src/configwidget.ui create mode 100644 src/gitmojis.json create mode 100644 src/gitmojis.qrc create mode 100644 src/plugin.cpp create mode 100644 src/plugin.h diff --git a/.gitignore b/.gitignore index bee8a64..d4e0134 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ __pycache__ +build/ +*.so +*.a +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +*.cmake +.ui_configwidget.h +.ui_*.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a732bd0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) +project(gitmoji VERSION 1.0.0) + +find_package(Albert REQUIRED) + +albert_plugin( + QT + Concurrent Widgets +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..50240dd --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Gitmoji Picker for Albert + +A fast and efficient C++ implementation of the Gitmoji picker for Albert launcher. + +## Features + +- Search gitmojis by name or description +- Copy either emoji or gitmoji code to clipboard +- Configurable copy format via settings +- Fast indexing and fuzzy search +- Native Albert C++ plugin + +## Installation + +### Building from source + +```bash +mkdir build && cd build +cmake .. +cmake --build . +``` + +### Installation + +Copy the built library to your Albert plugins directory: + +```bash +# Local installation +cp build/libgitmoji.so ~/.local/lib/albert/ + +# Or system-wide +sudo cp build/libgitmoji.so /usr/lib/albert/ +``` + +## Usage + +Trigger the plugin with `g:` followed by your search query. + +Examples: +- `g:bug` - Search for bug-related gitmojis +- `g:*` - Show all gitmojis +- `g:deploy` - Search for deployment-related gitmojis + +## Configuration + +Access the plugin settings in Albert to choose what to copy: +- **Emoji**: Copy the emoji character (e.g., 🎨) +- **Gitmoji code**: Copy the code (e.g., :art:) + +## Development + +This plugin uses: +- Albert C++ API +- Qt6 framework +- CMake build system +- IndexQueryHandler for efficient search + +## License + +MIT diff --git a/include/albert/plugin/gitmoji.h b/include/albert/plugin/gitmoji.h new file mode 100644 index 0000000..55b7005 --- /dev/null +++ b/include/albert/plugin/gitmoji.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +namespace gitmoji { + +class ALBERT_EXPORT Plugin : virtual public albert::Extension +{ +protected: + + virtual ~Plugin() = default; + +}; + +} diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..0aba9c6 --- /dev/null +++ b/metadata.json @@ -0,0 +1,7 @@ +{ + "name": "gitmoji picker", + "description": "Search and copy gitmojis to clipboard", + "authors": ["Andreas Schneider"], + "license": "MIT", + "url": "https://git.aksdb.de/aksdb/albert-gitmoji" +} diff --git a/src/configwidget.ui b/src/configwidget.ui new file mode 100644 index 0000000..6d66958 --- /dev/null +++ b/src/configwidget.ui @@ -0,0 +1,55 @@ + + + ConfigWidget + + + + 0 + 0 + 400 + 100 + + + + + + + Select what to copy to clipboard: + + + + + + + Emoji + + + + Emoji + + + + + Gitmoji code + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/gitmojis.json b/src/gitmojis.json new file mode 100644 index 0000000..f9f9d8d --- /dev/null +++ b/src/gitmojis.json @@ -0,0 +1,437 @@ +{ + "gitmojis":[ + { + "emoji":"🎨", + "entity":"🎨", + "code":":art:", + "description":"Improving structure / format of the code.", + "name":"art" + }, + { + "emoji":"⚑️", + "entity":"⚡", + "code":":zap:", + "description":"Improving performance.", + "name":"zap" + }, + { + "emoji":"πŸ”₯", + "entity":"🔥", + "code":":fire:", + "description":"Removing code or files.", + "name":"fire" + }, + { + "emoji":"πŸ›", + "entity":"🐛", + "code":":bug:", + "description":"Fixing a bug.", + "name":"bug" + }, + { + "emoji": "πŸš‘", + "entity": "🚑", + "code": ":ambulance:", + "description": "Critical hotfix.", + "name": "ambulance" + }, + { + "emoji":"✨", + "entity":"✨", + "code":":sparkles:", + "description":"Introducing new features.", + "name":"sparkles" + }, + { + "emoji":"πŸ“", + "entity":"📝", + "code":":memo:", + "description":"Writing docs.", + "name":"memo" + }, + { + "emoji":"πŸš€", + "entity":"🚀", + "code":":rocket:", + "description":"Deploying stuff.", + "name":"rocket" + }, + { + "emoji":"πŸ’„", + "entity":"&#ff99cc;", + "code":":lipstick:", + "description":"Updating the UI and style files.", + "name":"lipstick" + }, + { + "emoji":"πŸŽ‰", + "entity":"🎉", + "code":":tada:", + "description":"Initial commit.", + "name":"tada" + }, + { + "emoji":"βœ…", + "entity":"✅", + "code":":white_check_mark:", + "description":"Adding tests.", + "name":"white-check-mark" + }, + { + "emoji":"πŸ”’", + "entity":"🔒", + "code":":lock:", + "description":"Fixing security issues.", + "name":"lock" + }, + { + "emoji":"🍎", + "entity":"🍎", + "code":":apple:", + "description":"Fixing something on macOS.", + "name":"apple" + }, + { + "emoji":"🐧", + "entity":"🐧", + "code":":penguin:", + "description":"Fixing something on Linux.", + "name":"penguin" + }, + { + "emoji":"🏁", + "entity":"🏁", + "code":":checkered_flag:", + "description":"Fixing something on Windows.", + "name":"checkered-flag" + }, + { + "emoji":"πŸ€–", + "entity":"🤖", + "code":":robot:", + "description":"Fixing something on Android.", + "name":"robot" + }, + { + "emoji":"🍏", + "entity":"🍏", + "code":":green_apple:", + "description":"Fixing something on iOS.", + "name":"green-apple" + }, + { + "emoji":"πŸ”–", + "entity":"🔖", + "code":":bookmark:", + "description":"Releasing / Version tags.", + "name":"bookmark" + }, + { + "emoji":"🚨", + "entity":"🚨", + "code":":rotating_light:", + "description":"Removing linter warnings.", + "name":"rotating-light" + }, + { + "emoji":"🚧", + "entity":"🚧", + "code":":construction:", + "description":"Work in progress.", + "name":"construction" + }, + { + "emoji":"πŸ’š", + "entity":"💚", + "code":":green_heart:", + "description":"Fixing CI Build.", + "name":"green-heart" + }, + { + "emoji":"⬇️", + "entity":"⬇️", + "code":":arrow_down:", + "description":"Downgrading dependencies.", + "name":"arrow-down" + }, + { + "emoji":"⬆️", + "entity":"⬆️", + "code":":arrow_up:", + "description":"Upgrading dependencies.", + "name":"arrow-up" + }, + { + "emoji": "πŸ“Œ", + "entity": "📌", + "code": ":pushpin:", + "description": "Pinning dependencies to specific versions.", + "name": "pushpin" + }, + { + "emoji":"πŸ‘·", + "entity":"👷", + "code":":construction_worker:", + "description":"Adding CI build system.", + "name":"construction-worker" + }, + { + "emoji":"πŸ“ˆ", + "code":":chart_with_upwards_trend:", + "description":"Adding analytics or tracking code.", + "name":"chart-with-upwards-trend" + }, + { + "emoji":"♻️", + "entity":"♲", + "code":":recycle:", + "description":"Refactoring code.", + "name":"recycle" + }, + { + "emoji":"🐳", + "entity":"🐳", + "code":":whale:", + "description":"Work about Docker.", + "name":"whale" + }, + { + "emoji":"βž•", + "entity":"➕", + "code":":heavy_plus_sign:", + "description":"Adding a dependency.", + "name":"heavy-plus-sign" + }, + { + "emoji":"βž–", + "entity":"➖", + "code":":heavy_minus_sign:", + "description":"Removing a dependency.", + "name":"heavy-minus-sign" + }, + { + "emoji":"πŸ”§", + "entity":"🔧", + "code":":wrench:", + "description":"Changing configuration files.", + "name":"wrench" + }, + { + "emoji": "🌐", + "entity": "🌐", + "code": ":globe_with_meridians:", + "description": "Internationalization and localization.", + "name": "globe-with-meridians" + }, + { + "emoji":"✏️", + "entity":"", + "code":":pencil2:", + "description":"Fixing typos.", + "name":"pencil" + }, + { + "emoji":"πŸ’©", + "entity":"", + "code":":hankey:", + "description":"Writing bad code that needs to be improved.", + "name":"hankey" + }, + { + "emoji":"βͺ", + "entity":"⏪", + "code":":rewind:", + "description":"Reverting changes.", + "name":"rewind" + }, + { + "emoji":"πŸ”€", + "entity":"🔀", + "code":":twisted_rightwards_arrows:", + "description":"Merging branches.", + "name":"twisted-rightwards-arrows" + }, + { + "emoji":"πŸ“¦", + "entity":"F4E6;", + "code":":package:", + "description":"Updating compiled files or packages.", + "name":"package" + }, + { + "emoji":"πŸ‘½", + "entity":"F47D;", + "code":":alien:", + "description":"Updating code due to external API changes.", + "name":"alien" + }, + { + "emoji":"🚚", + "entity":"F69A;", + "code":":truck:", + "description":"Moving or renaming files.", + "name":"truck" + }, + { + "emoji":"πŸ“„", + "entity":"F4C4;", + "code":":page_facing_up:", + "description":"Adding or updating license.", + "name":"page-facing-up" + }, + { + "emoji":"πŸ’₯", + "entity":"💥", + "code":":boom:", + "description":"Introducing breaking changes.", + "name":"boom" + }, + { + "emoji":"🍱", + "entity":"F371", + "code":":bento:", + "description":"Adding or updating assets.", + "name":"bento" + }, + { + "emoji":"πŸ‘Œ", + "entity":"👌", + "code":":ok_hand:", + "description":"Updating code due to code review changes.", + "name":"ok-hand" + }, + { + "emoji": "♿️", + "entity":"♿", + "code": ":wheelchair:", + "description":"Improving accessibility.", + "name":"wheelchair" + }, + { + "emoji": "πŸ’‘", + "entity":"💡", + "code": ":bulb:", + "description":"Documenting source code.", + "name":"bulb" + }, + { + "emoji": "🍻", + "entity":"🍻", + "code": ":beers:", + "description": "Writing code drunkenly.", + "name":"beers" + }, + { + "emoji": "πŸ’¬", + "entity":"💬", + "code": ":speech_balloon:", + "description": "Updating text and literals.", + "name":"speech-balloon" + }, + { + "emoji": "πŸ—ƒ", + "entity":"🗃", + "code": ":card_file_box:", + "description": "Performing database related changes.", + "name":"card-file-box" + }, + { + "emoji":"πŸ”Š", + "entity":"🔊", + "code":":loud_sound:", + "description":"Adding logs.", + "name":"loud-sound" + }, + { + "emoji":"πŸ”‡", + "entity":"🔇", + "code":":mute:", + "description":"Removing logs.", + "name":"mute" + }, + { + "emoji": "πŸ‘₯", + "entity": "👥", + "code": ":busts_in_silhouette:", + "description": "Adding contributor(s).", + "name": "busts-in-silhouette" + }, + { + "emoji": "🚸", + "entity": "🚸", + "code": ":children_crossing:", + "description": "Improving user experience / usability.", + "name": "children-crossing" + }, + { + "emoji": "πŸ—", + "entity": "f3d7;", + "code": ":building_construction:", + "description": "Making architectural changes.", + "name": "building-construction" + }, + { + "emoji": "πŸ“±", + "entity": "📱" , + "code": ":iphone:", + "description": "Working on responsive design.", + "name": "iphone" + }, + { + "emoji": "🀑", + "entity": "🤡" , + "code": ":clown_face:", + "description": "Mocking things.", + "name": "clown-face" + }, + { + "emoji": "πŸ₯š", + "entity": "🥚", + "code": ":egg:", + "description": "Adding an easter egg.", + "name": "egg" + }, + { + "emoji": "πŸ™ˆ", + "entity": "bdfe7;" , + "code": ":see_no_evil:", + "description": "Adding or updating a .gitignore file", + "name": "see-no-evil" + }, + { + "emoji": "πŸ“Έ", + "entity": "📸" , + "code": ":camera_flash:", + "description": "Adding or updating snapshots", + "name": "camera-flash" + }, + { + "emoji": "βš—", + "entity": "📸" , + "code": ":alembic:", + "description": "Experimenting new things", + "name": "alembic" + }, + { + "emoji": "πŸ”", + "entity": "🔍" , + "code": ":mag:", + "description": "Improving SEO", + "name": "mag" + }, + { + "emoji":"☸️", + "entity":"☸", + "code":":wheel_of_dharma:", + "description":"Work about Kubernetes", + "name":"wheel-of-dharma" + }, + { + "emoji": "🏷️", + "entity": "🏷", + "code": ":label:", + "description": "Adding or updating types (Flow, TypeScript)", + "name": "label" + } + ] +} diff --git a/src/gitmojis.qrc b/src/gitmojis.qrc new file mode 100644 index 0000000..f3ef850 --- /dev/null +++ b/src/gitmojis.qrc @@ -0,0 +1,5 @@ + + + gitmojis.json + + diff --git a/src/plugin.cpp b/src/plugin.cpp new file mode 100644 index 0000000..861b735 --- /dev/null +++ b/src/plugin.cpp @@ -0,0 +1,169 @@ +#include "plugin.h" +#include "ui_configwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ALBERT_LOGGING_CATEGORY("gitmoji") +using namespace Qt::StringLiterals; +using namespace albert; +using namespace std; + +static const QString CONFIG_KEY = u"copy_format"_s; +static const QString VALUE_EMOJI = u"emoji"_s; +static const QString VALUE_CODE = u"code"_s; + +struct GitmojiItem : Item +{ + QString id_; + QString emoji_; + QString code_; + QString description_; + + GitmojiItem(const QString& emoji, const QString& code, const QString& description) + : emoji_(emoji), code_(code), description_(description) + { + id_ = code.mid(1, code.length() - 2); + } + + QString id() const override { return id_; } + + QString text() const override + { + return u"%1 %2"_s.arg(emoji_, code_); + } + + QString subtext() const override + { + return description_; + } + + std::unique_ptr icon() const override + { + return Icon::grapheme(emoji_); + } + + vector actions() const override + { + vector actions; + + QSettings settings; + QString copyFormat = settings.value(CONFIG_KEY, VALUE_EMOJI).toString(); + + if (copyFormat == VALUE_EMOJI) + { + actions.emplace_back(u"emoji"_s, u"Copy emoji"_s, + [this] { setClipboardText(emoji_); }); + } + else + { + actions.emplace_back(u"code"_s, u"Copy gitmoji code"_s, + [this] { setClipboardText(code_); }); + } + + return actions; + } +}; + +Plugin::Plugin() +{ + indexer.parallel = [this](const bool &abort) + { + vector r; + + QFile file(u":/gitmojis.json"_s); + if (!file.open(QIODevice::ReadOnly)) + { + WARN << "Failed to open gitmojis.json"; + return r; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + file.close(); + + if (!doc.isObject()) + { + WARN << "Invalid gitmojis.json format"; + return r; + } + + QJsonObject root = doc.object(); + QJsonArray gitmojis = root[u"gitmojis"_s].toArray(); + + for (const QJsonValue& value : gitmojis) + { + if (abort) return r; + + QJsonObject gitmoji = value.toObject(); + QString emoji = gitmoji[u"emoji"_s].toString(); + QString code = gitmoji[u"code"_s].toString(); + QString description = gitmoji[u"description"_s].toString(); + + if (emoji.isEmpty() || code.isEmpty()) + continue; + + auto item = make_shared(emoji, code, description); + + QString name = gitmoji[u"name"_s].toString(); + QString searchable = u"%1 %2"_s.arg(name, description); + + r.emplace_back(item, searchable); + } + + INFO << u"Indexed %1 gitmojis."_s.arg(r.size()); + return r; + }; + + indexer.finish = [this] + { + auto index_items = indexer.takeResult(); + setIndexItems(::move(index_items)); + }; +} + +QString Plugin::defaultTrigger() const +{ + return u"g:"_s; +} + +QString Plugin::synopsis(const QString &) const +{ + return tr("||*"); +} + +void Plugin::updateIndexItems() +{ + indexer.run(); +} + +vector Plugin::rankItems(QueryContext &ctx) +{ + return IndexQueryHandler::rankItems(ctx); +} + +QWidget *Plugin::buildConfigWidget() +{ + auto *widget = new QWidget; + Ui::ConfigWidget ui; + ui.setupUi(widget); + + QSettings settings; + QString currentFormat = settings.value(CONFIG_KEY, VALUE_EMOJI).toString(); + + int index = (currentFormat == VALUE_CODE) ? 1 : 0; + ui.comboBox->setCurrentIndex(index); + + connect(ui.comboBox, QOverload::of(&QComboBox::currentIndexChanged), + [ui](int comboBoxIndex) { + QSettings pluginSettings; + pluginSettings.setValue(CONFIG_KEY, (comboBoxIndex == 1) ? VALUE_CODE : VALUE_EMOJI); + }); + + return widget; +} diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..a621b34 --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +class QWidget; + +class Plugin : public albert::ExtensionPlugin, + public albert::IndexQueryHandler +{ + ALBERT_PLUGIN + +public: + Plugin(); + +private: + QString defaultTrigger() const override; + QWidget* buildConfigWidget() override; + void updateIndexItems() override; + QString synopsis(const QString &) const override; + std::vector rankItems(albert::QueryContext &) override; + + albert::BackgroundExecutor> indexer; +};