Compare commits

7 Commits
master ... main

Author SHA1 Message Date
56aa0d5017 📝 Adjust README to not conflict with markdown rendering 2026-03-01 11:13:15 +01:00
773d30c2d0 💄 Show name instead of duplicating the icon 2026-03-01 11:11:04 +01:00
763eb7e85d 🍱 Update gitmojis 2026-03-01 11:08:30 +01:00
3a5ecc9ff2 🐛 Include gitmojis in build 2026-03-01 11:07:10 +01:00
6a182a0025 ♻️ Improve trigger 2026-03-01 11:07:10 +01:00
5864b83c25 🔥 Remove old python code 2026-03-01 11:07:10 +01:00
04823e1a7a ♻️ Convert plugin to c++ 2026-03-01 11:07:10 +01:00
11 changed files with 983 additions and 69 deletions

9
.gitignore vendored
View File

@@ -1 +1,10 @@
__pycache__ __pycache__
build/
*.so
*.a
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
*.cmake
.ui_configwidget.h
.ui_*.h

18
CMakeLists.txt Normal file
View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.16)
project(gitmoji VERSION 1.0.0)
find_package(Albert REQUIRED)
albert_plugin(
INCLUDE
INTERFACE include
PRIVATE include/albert/plugin
QT
Concurrent Widgets
)
# Add the resource file manually
qt6_add_resources(RESOURCES src/gitmojis.qrc)
# Add resources to the target
target_sources(gitmoji PRIVATE ${RESOURCES})

66
README.md Normal file
View File

@@ -0,0 +1,66 @@
# 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
- Gitmoji data embedded as Qt resource (no external files needed)
## 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/gitmoji.so ~/.local/lib/albert/
# Or system-wide
sudo cp build/gitmoji.so /usr/lib/albert/
```
Restart Albert to load the plugin.
## Usage
Trigger the plugin with `gm ` followed by your search query.
Examples:
- `gm bug` - Search for bug-related gitmojis
- `gm *` - Show all gitmojis
- `gm deploy` - Search for deployment-related gitmojis
## Configuration
Access plugin settings in Albert to choose what to copy:
- **Emoji**: Copy emoji character (e.g., 🎨)
- **Gitmoji code**: Copy code (e.g., `:art:`)
Settings are persisted using QSettings.
## Development
This plugin uses:
- Albert C++ API
- Qt6 framework with embedded resources
- CMake build system
- IndexQueryHandler for efficient search
- BackgroundExecutor for async indexing
## License
MIT

View File

@@ -1,69 +0,0 @@
# -*- coding: utf-8 -*-
"""gitmoji Picker for Albert
Usage: g: gitmoji name or description
Examples:
g:bug
g:*"""
from albertv0 import *
import os
import json
__iid__ = "PythonInterface/v0.2"
__prettyname__ = "gitmoji picker"
__version__ = "1.0.0"
__trigger__ = "g:"
__author__ = "Andreas Schneider"
__dependencies__ = []
data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "gitmojis.json")
gitmojis = []
def initialize():
with open(data_path) as f:
data = json.load(f)
gitmojis.clear()
for gitmoji in data["gitmojis"]:
gitmoji["tokens"] = [gitmoji["name"].lower()] + gitmoji["description"].lower().split()
gitmojis.append(gitmoji)
def handleQuery(query):
if not query.isValid or not query.isTriggered:
return []
if query.string == "*":
matches = gitmojis
else:
needles = query.string.lower().split()
matches = []
for gitmoji in gitmojis:
matchCount = count_matches(gitmoji["tokens"], needles)
if matchCount > 0:
result = dict()
result.update(gitmoji)
result["matchCount"] = matchCount
matches.append(result)
if not query.isValid:
return []
matches = sorted(matches, key=lambda data: data["matchCount"], reverse=True)
if query.isValid:
return [Item(id=match["name"], completion=match["name"], icon=match["emoji"], text=match["emoji"] + " " + match["code"], subtext=match["description"], actions=[ClipAction("Copy to clipboard", match["emoji"])]) for match in matches]
else:
return []
def count_matches(tokens, needles):
count = 0
for token in tokens:
for needle in needles:
if needle in token:
count += 1
return count

View File

@@ -0,0 +1,15 @@
#pragma once
#include <albert/export.h>
#include <albert/extension.h>
namespace gitmoji {
class ALBERT_EXPORT Plugin : virtual public albert::Extension
{
protected:
virtual ~Plugin() = default;
};
}

7
metadata.json Normal file
View File

@@ -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"
}

55
src/configwidget.ui Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigWidget</class>
<widget class="QWidget" name="ConfigWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>100</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Select what to copy to clipboard:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="currentText">
<string>Emoji</string>
</property>
<item>
<property name="text">
<string>Emoji</string>
</property>
</item>
<item>
<property name="text">
<string>Gitmoji code</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

605
src/gitmojis.json Normal file
View File

@@ -0,0 +1,605 @@
{
"$schema": "https://gitmoji.dev/api/gitmojis/schema",
"gitmojis": [
{
"emoji": "🎨",
"entity": "&#x1f3a8;",
"code": ":art:",
"description": "Improve structure / format of the code.",
"name": "art",
"semver": null
},
{
"emoji": "⚡️",
"entity": "&#x26a1;",
"code": ":zap:",
"description": "Improve performance.",
"name": "zap",
"semver": "patch"
},
{
"emoji": "🔥",
"entity": "&#x1f525;",
"code": ":fire:",
"description": "Remove code or files.",
"name": "fire",
"semver": null
},
{
"emoji": "🐛",
"entity": "&#x1f41b;",
"code": ":bug:",
"description": "Fix a bug.",
"name": "bug",
"semver": "patch"
},
{
"emoji": "🚑️",
"entity": "&#128657;",
"code": ":ambulance:",
"description": "Critical hotfix.",
"name": "ambulance",
"semver": "patch"
},
{
"emoji": "✨",
"entity": "&#x2728;",
"code": ":sparkles:",
"description": "Introduce new features.",
"name": "sparkles",
"semver": "minor"
},
{
"emoji": "📝",
"entity": "&#x1f4dd;",
"code": ":memo:",
"description": "Add or update documentation.",
"name": "memo",
"semver": null
},
{
"emoji": "🚀",
"entity": "&#x1f680;",
"code": ":rocket:",
"description": "Deploy stuff.",
"name": "rocket",
"semver": null
},
{
"emoji": "💄",
"entity": "&#ff99cc;",
"code": ":lipstick:",
"description": "Add or update the UI and style files.",
"name": "lipstick",
"semver": "patch"
},
{
"emoji": "🎉",
"entity": "&#127881;",
"code": ":tada:",
"description": "Begin a project.",
"name": "tada",
"semver": null
},
{
"emoji": "✅",
"entity": "&#x2705;",
"code": ":white_check_mark:",
"description": "Add, update, or pass tests.",
"name": "white-check-mark",
"semver": null
},
{
"emoji": "🔒️",
"entity": "&#x1f512;",
"code": ":lock:",
"description": "Fix security or privacy issues.",
"name": "lock",
"semver": "patch"
},
{
"emoji": "🔐",
"entity": "&#x1f510;",
"code": ":closed_lock_with_key:",
"description": "Add or update secrets.",
"name": "closed-lock-with-key",
"semver": null
},
{
"emoji": "🔖",
"entity": "&#x1f516;",
"code": ":bookmark:",
"description": "Release / Version tags.",
"name": "bookmark",
"semver": null
},
{
"emoji": "🚨",
"entity": "&#x1f6a8;",
"code": ":rotating_light:",
"description": "Fix compiler / linter warnings.",
"name": "rotating-light",
"semver": null
},
{
"emoji": "🚧",
"entity": "&#x1f6a7;",
"code": ":construction:",
"description": "Work in progress.",
"name": "construction",
"semver": null
},
{
"emoji": "💚",
"entity": "&#x1f49a;",
"code": ":green_heart:",
"description": "Fix CI Build.",
"name": "green-heart",
"semver": null
},
{
"emoji": "⬇️",
"entity": "⬇️",
"code": ":arrow_down:",
"description": "Downgrade dependencies.",
"name": "arrow-down",
"semver": "patch"
},
{
"emoji": "⬆️",
"entity": "⬆️",
"code": ":arrow_up:",
"description": "Upgrade dependencies.",
"name": "arrow-up",
"semver": "patch"
},
{
"emoji": "📌",
"entity": "&#x1F4CC;",
"code": ":pushpin:",
"description": "Pin dependencies to specific versions.",
"name": "pushpin",
"semver": "patch"
},
{
"emoji": "👷",
"entity": "&#x1f477;",
"code": ":construction_worker:",
"description": "Add or update CI build system.",
"name": "construction-worker",
"semver": null
},
{
"emoji": "📈",
"entity": "&#x1F4C8;",
"code": ":chart_with_upwards_trend:",
"description": "Add or update analytics or track code.",
"name": "chart-with-upwards-trend",
"semver": "patch"
},
{
"emoji": "♻️",
"entity": "&#x267b;",
"code": ":recycle:",
"description": "Refactor code.",
"name": "recycle",
"semver": null
},
{
"emoji": "",
"entity": "&#10133;",
"code": ":heavy_plus_sign:",
"description": "Add a dependency.",
"name": "heavy-plus-sign",
"semver": "patch"
},
{
"emoji": "",
"entity": "&#10134;",
"code": ":heavy_minus_sign:",
"description": "Remove a dependency.",
"name": "heavy-minus-sign",
"semver": "patch"
},
{
"emoji": "🔧",
"entity": "&#x1f527;",
"code": ":wrench:",
"description": "Add or update configuration files.",
"name": "wrench",
"semver": "patch"
},
{
"emoji": "🔨",
"entity": "&#128296;",
"code": ":hammer:",
"description": "Add or update development scripts.",
"name": "hammer",
"semver": null
},
{
"emoji": "🌐",
"entity": "&#127760;",
"code": ":globe_with_meridians:",
"description": "Internationalization and localization.",
"name": "globe-with-meridians",
"semver": "patch"
},
{
"emoji": "✏️",
"entity": "&#59161;",
"code": ":pencil2:",
"description": "Fix typos.",
"name": "pencil2",
"semver": "patch"
},
{
"emoji": "💩",
"entity": "&#58613;",
"code": ":poop:",
"description": "Write bad code that needs to be improved.",
"name": "poop",
"semver": null
},
{
"emoji": "⏪️",
"entity": "&#9194;",
"code": ":rewind:",
"description": "Revert changes.",
"name": "rewind",
"semver": "patch"
},
{
"emoji": "🔀",
"entity": "&#128256;",
"code": ":twisted_rightwards_arrows:",
"description": "Merge branches.",
"name": "twisted-rightwards-arrows",
"semver": null
},
{
"emoji": "📦️",
"entity": "&#1F4E6;",
"code": ":package:",
"description": "Add or update compiled files or packages.",
"name": "package",
"semver": "patch"
},
{
"emoji": "👽️",
"entity": "&#1F47D;",
"code": ":alien:",
"description": "Update code due to external API changes.",
"name": "alien",
"semver": "patch"
},
{
"emoji": "🚚",
"entity": "&#1F69A;",
"code": ":truck:",
"description": "Move or rename resources (e.g.: files, paths, routes).",
"name": "truck",
"semver": null
},
{
"emoji": "📄",
"entity": "&#1F4C4;",
"code": ":page_facing_up:",
"description": "Add or update license.",
"name": "page-facing-up",
"semver": null
},
{
"emoji": "💥",
"entity": "&#x1f4a5;",
"code": ":boom:",
"description": "Introduce breaking changes.",
"name": "boom",
"semver": "major"
},
{
"emoji": "🍱",
"entity": "&#1F371",
"code": ":bento:",
"description": "Add or update assets.",
"name": "bento",
"semver": "patch"
},
{
"emoji": "♿️",
"entity": "&#9855;",
"code": ":wheelchair:",
"description": "Improve accessibility.",
"name": "wheelchair",
"semver": "patch"
},
{
"emoji": "💡",
"entity": "&#128161;",
"code": ":bulb:",
"description": "Add or update comments in source code.",
"name": "bulb",
"semver": null
},
{
"emoji": "🍻",
"entity": "&#x1f37b;",
"code": ":beers:",
"description": "Write code drunkenly.",
"name": "beers",
"semver": null
},
{
"emoji": "💬",
"entity": "&#128172;",
"code": ":speech_balloon:",
"description": "Add or update text and literals.",
"name": "speech-balloon",
"semver": "patch"
},
{
"emoji": "🗃️",
"entity": "&#128451;",
"code": ":card_file_box:",
"description": "Perform database related changes.",
"name": "card-file-box",
"semver": "patch"
},
{
"emoji": "🔊",
"entity": "&#128266;",
"code": ":loud_sound:",
"description": "Add or update logs.",
"name": "loud-sound",
"semver": null
},
{
"emoji": "🔇",
"entity": "&#128263;",
"code": ":mute:",
"description": "Remove logs.",
"name": "mute",
"semver": null
},
{
"emoji": "👥",
"entity": "&#128101;",
"code": ":busts_in_silhouette:",
"description": "Add or update contributor(s).",
"name": "busts-in-silhouette",
"semver": null
},
{
"emoji": "🚸",
"entity": "&#128696;",
"code": ":children_crossing:",
"description": "Improve user experience / usability.",
"name": "children-crossing",
"semver": "patch"
},
{
"emoji": "🏗️",
"entity": "&#1f3d7;",
"code": ":building_construction:",
"description": "Make architectural changes.",
"name": "building-construction",
"semver": null
},
{
"emoji": "📱",
"entity": "&#128241;",
"code": ":iphone:",
"description": "Work on responsive design.",
"name": "iphone",
"semver": "patch"
},
{
"emoji": "🤡",
"entity": "&#129313;",
"code": ":clown_face:",
"description": "Mock things.",
"name": "clown-face",
"semver": null
},
{
"emoji": "🥚",
"entity": "&#129370;",
"code": ":egg:",
"description": "Add or update an easter egg.",
"name": "egg",
"semver": "patch"
},
{
"emoji": "🙈",
"entity": "&#8bdfe7;",
"code": ":see_no_evil:",
"description": "Add or update a .gitignore file.",
"name": "see-no-evil",
"semver": null
},
{
"emoji": "📸",
"entity": "&#128248;",
"code": ":camera_flash:",
"description": "Add or update snapshots.",
"name": "camera-flash",
"semver": null
},
{
"emoji": "⚗️",
"entity": "&#x2697;",
"code": ":alembic:",
"description": "Perform experiments.",
"name": "alembic",
"semver": "patch"
},
{
"emoji": "🔍️",
"entity": "&#128269;",
"code": ":mag:",
"description": "Improve SEO.",
"name": "mag",
"semver": "patch"
},
{
"emoji": "🏷️",
"entity": "&#127991;",
"code": ":label:",
"description": "Add or update types.",
"name": "label",
"semver": "patch"
},
{
"emoji": "🌱",
"entity": "&#127793;",
"code": ":seedling:",
"description": "Add or update seed files.",
"name": "seedling",
"semver": null
},
{
"emoji": "🚩",
"entity": "&#x1F6A9;",
"code": ":triangular_flag_on_post:",
"description": "Add, update, or remove feature flags.",
"name": "triangular-flag-on-post",
"semver": "patch"
},
{
"emoji": "🥅",
"entity": "&#x1F945;",
"code": ":goal_net:",
"description": "Catch errors.",
"name": "goal-net",
"semver": "patch"
},
{
"emoji": "💫",
"entity": "&#x1f4ab;",
"code": ":dizzy:",
"description": "Add or update animations and transitions.",
"name": "dizzy",
"semver": "patch"
},
{
"emoji": "🗑️",
"entity": "&#x1F5D1;",
"code": ":wastebasket:",
"description": "Deprecate code that needs to be cleaned up.",
"name": "wastebasket",
"semver": "patch"
},
{
"emoji": "🛂",
"entity": "&#x1F6C2;",
"code": ":passport_control:",
"description": "Work on code related to authorization, roles and permissions.",
"name": "passport-control",
"semver": "patch"
},
{
"emoji": "🩹",
"entity": "&#x1FA79;",
"code": ":adhesive_bandage:",
"description": "Simple fix for a non-critical issue.",
"name": "adhesive-bandage",
"semver": "patch"
},
{
"emoji": "🧐",
"entity": "&#x1F9D0;",
"code": ":monocle_face:",
"description": "Data exploration/inspection.",
"name": "monocle-face",
"semver": null
},
{
"emoji": "⚰️",
"entity": "&#x26B0;",
"code": ":coffin:",
"description": "Remove dead code.",
"name": "coffin",
"semver": null
},
{
"emoji": "🧪",
"entity": "&#x1F9EA;",
"code": ":test_tube:",
"description": "Add a failing test.",
"name": "test-tube",
"semver": null
},
{
"emoji": "👔",
"entity": "&#128084;",
"code": ":necktie:",
"description": "Add or update business logic.",
"name": "necktie",
"semver": "patch"
},
{
"emoji": "🩺",
"entity": "&#x1FA7A;",
"code": ":stethoscope:",
"description": "Add or update healthcheck.",
"name": "stethoscope",
"semver": null
},
{
"emoji": "🧱",
"entity": "&#x1f9f1;",
"code": ":bricks:",
"description": "Infrastructure related changes.",
"name": "bricks",
"semver": null
},
{
"emoji": "🧑‍💻",
"entity": "&#129489;&#8205;&#128187;",
"code": ":technologist:",
"description": "Improve developer experience.",
"name": "technologist",
"semver": null
},
{
"emoji": "💸",
"entity": "&#x1F4B8;",
"code": ":money_with_wings:",
"description": "Add sponsorships or money related infrastructure.",
"name": "money-with-wings",
"semver": null
},
{
"emoji": "🧵",
"entity": "&#x1F9F5;",
"code": ":thread:",
"description": "Add or update code related to multithreading or concurrency.",
"name": "thread",
"semver": null
},
{
"emoji": "🦺",
"entity": "&#x1F9BA;",
"code": ":safety_vest:",
"description": "Add or update code related to validation.",
"name": "safety-vest",
"semver": null
},
{
"emoji": "✈️",
"entity": "&#x2708;",
"code": ":airplane:",
"description": "Improve offline support.",
"name": "airplane",
"semver": null
},
{
"emoji": "🦖",
"entity": "&#x2708;",
"code": ":t-rex:",
"description": "Code that adds backwards compatibility.",
"name": "t-rex",
"semver": null
}
]
}

5
src/gitmojis.qrc Normal file
View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>gitmojis.json</file>
</qresource>
</RCC>

178
src/plugin.cpp Normal file
View File

@@ -0,0 +1,178 @@
#include "plugin.h"
#include "ui_configwidget.h"
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSettings>
#include <albert/icon.h>
#include <albert/logging.h>
#include <albert/standarditem.h>
#include <albert/systemutil.h>
ALBERT_LOGGING_CATEGORY("gitmoji")
using namespace Qt::StringLiterals;
using namespace albert;
using namespace std;
static bool resourcesInitialized = false;
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 name_;
QString emoji_;
QString code_;
QString description_;
GitmojiItem(const QString& name, const QString& emoji, const QString& code, const QString& description)
: name_(name), emoji_(emoji), code_(code), description_(description)
{
id_ = code.mid(1, code.length() - 2);
}
QString id() const override { return id_; }
QString text() const override
{
return name_;
}
QString subtext() const override
{
return description_;
}
std::unique_ptr<Icon> icon() const override
{
return Icon::grapheme(emoji_);
}
vector<Action> actions() const override
{
vector<Action> 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()
{
if (!resourcesInitialized)
{
Q_INIT_RESOURCE(gitmojis);
resourcesInitialized = true;
}
indexer.parallel = [this](const bool &abort)
{
vector<IndexItem> 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 name = gitmoji[u"name"_s].toString();
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<GitmojiItem>(name, emoji, code, description);
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"gm "_s;
}
QString Plugin::synopsis(const QString &) const
{
return tr("<gitmoji name>|<gitmoji description>|*");
}
void Plugin::updateIndexItems()
{
indexer.run();
}
vector<RankItem> 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<int>::of(&QComboBox::currentIndexChanged),
[ui](int comboBoxIndex) {
QSettings pluginSettings;
pluginSettings.setValue(CONFIG_KEY, (comboBoxIndex == 1) ? VALUE_CODE : VALUE_EMOJI);
});
return widget;
}

25
src/plugin.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <albert/backgroundexecutor.h>
#include <albert/extensionplugin.h>
#include <albert/indexqueryhandler.h>
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<albert::RankItem> rankItems(albert::QueryContext &) override;
albert::BackgroundExecutor<std::vector<albert::IndexItem>> indexer;
};