Logo

dev-resources.site

for different kinds of informations.

Develop a ulauncher extension with a command database

Published at
1/6/2025
Categories
vim
ulauncher
python
json
Author
ubuntupunk
Categories
4 categories in total
vim
open
ulauncher
open
python
open
json
open
Author
10 person written this
ubuntupunk
open
Develop a ulauncher extension with a command database

Over the weekend, I picked up a project via Reddit involving a plugin for the Flow Launcher. I created a fzf and rofi version for my Ubuntu Linux environment, and then thought, how hard can it be to port it to uLauncher?

Image description

Here I document what I did.

1. Inside ~/.local/share/ulauncher/extensions/

create a new dir. In my case, I created ~/.local/share/ulauncher/extensions/com.github.ubuntupunk.ulauncher-vim

2. Touch the following files:

β”œβ”€β”€ images
β”‚   └── icon.png
β”œβ”€β”€ versions.json
β”œβ”€β”€ manifest.json
└── main.py
Enter fullscreen mode Exit fullscreen mode

3. In versions.json place the following boilerplate:

[
  {"required_api_version": "2", "commit": "master"}
]
Enter fullscreen mode Exit fullscreen mode

4. In manifest.json

{
  "required_api_version": "2",
  "name": "Demo extension",
  "description": "Extension Description",
  "developer_name": "John Doe",
  "icon": "images/icon.png",
  "options": {
    "query_debounce": 0.1
  },
  "preferences": [
    {
      "id": "demo_kw",
      "type": "keyword",
      "name": "Demo",
      "description": "Demo extension",
      "default_value": "dm"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

5. In main.py

from ulauncher.api.client.Extension import Extension
from ulauncher.api.client.EventListener import EventListener
from ulauncher.api.shared.event import KeywordQueryEvent, ItemEnterEvent
from ulauncher.api.shared.item.ExtensionResultItem import ExtensionResultItem
from ulauncher.api.shared.action.RenderResultListAction import RenderResultListAction
from ulauncher.api.shared.action.HideWindowAction import HideWindowAction


class DemoExtension(Extension):

    def __init__(self):
        super().__init__()
        self.subscribe(KeywordQueryEvent, KeywordQueryEventListener())


class KeywordQueryEventListener(EventListener):

    def on_event(self, event, extension):
        items = []
        for i in range(5):
            items.append(ExtensionResultItem(icon='images/icon.png',
                                             name='Item %s' % i,
                                             description='Item description %s' % i,
                                             on_enter=HideWindowAction()))

        return RenderResultListAction(items)

if __name__ == '__main__':
    DemoExtension().run()
Enter fullscreen mode Exit fullscreen mode

6. Now edit manifest.json

{
  "required_api_version": "2",
  "name": "Vim Prompter",
  "description": "Vim cheatsheet helper",
  "developer_name": "David Robert Lewis",
  "icon": "images/icon.png",
  "options": {
    "query_debounce": 0.1
  },
  "preferences": [
    {
      "id": "vm_kw",
      "type": "keyword",
      "name": "Vim",
      "description": "Search for Vim commands",
      "default_value": "vm"
    }
  ]
Enter fullscreen mode Exit fullscreen mode

7. Add command loading function to main.py

class VmExtension(Extension):
    def load_vim_commands(self):
        """Load Vim commands from JSON file."""
        package_dir = os.path.dirname(os.path.abspath(__file__))
        full_path = os.path.join(package_dir, 'db', 'commands.json')   
        with open(full_path, 'r') as file:
            return json.load(file)

    def __init__(self):
        super().__init__()
        self.vim_commands = self.load_vim_commands()
        self.subscribe(KeywordQueryEvent, KeywordQueryEventListener())
Enter fullscreen mode Exit fullscreen mode

8. Create a db folder with the following:

commands.json

example structure:

{
  "categories": {
    "navigation": {
      "name": "Navigation",
      "patterns": [
        "scroll",
        "jump",
        "goto",
        "position"
      ],
      "subcategories": {
        "cursor": {
          "name": "Cursor Movement",
          "patterns": [
            "move[s]? cursor",
            "^[hjkl]$",
            "^[HJKL]$",
            "^[wWeEbB]$"
          ]
        },
Enter fullscreen mode Exit fullscreen mode

You can see the entire commands.json here.

9. Modify the KeywordQueryEventListener to implement the search functionality.

class KeywordQueryEventListener(EventListener):
    def on_event(self, event, extension):
        query = event.get_argument() or ""
        items = []

        # If no query, show all commands (limited to first 8)
        commands_to_show = extension.vim_commands

        # If there's a query, filter commands
        if query:
            commands_to_show = [
                cmd for cmd in extension.vim_commands
                if query.lower() in cmd['command'].lower() or 
                   query.lower() in cmd['description'].lower()
            ]

        # Limit results to first 8 matches
        for cmd in commands_to_show[:8]:
            items.append(ExtensionResultItem(
                icon='images/icon.png',
                name=cmd['command'],
                description=f"{cmd['name']} - {cmd['description']}",
                on_enter=HideWindowAction()
            ))

        return RenderResultListAction(items)
Enter fullscreen mode Exit fullscreen mode

10. Add the URL opening functionality. We'll need to import webbrowser and modify the on_enter action to open the Vim command URL

from ulauncher.api.shared.action.OpenUrlAction import OpenUrlAction

class KeywordQueryEventListener(EventListener):
    def on_event(self, event, extension):
        query = event.get_argument() or ""
        items = []

        commands_to_show = extension.vim_commands

        if query:
            commands_to_show = [
                cmd for cmd in extension.vim_commands
                if query.lower() in cmd['command'].lower() or 
                   query.lower() in cmd['description'].lower()
            ]

        for cmd in commands_to_show[:8]:
            url = f"https://vim.rtorr.com/#:~:text={cmd['rtorr_description']}"
            items.append(ExtensionResultItem(
                icon='images/icon.png',
                name=cmd['command'],
                description=f"{cmd['name']} - {cmd['description']}",
                on_enter=OpenUrlAction(url)
            ))

        return RenderResultListAction(items)
Enter fullscreen mode Exit fullscreen mode

11. Key changes are:

  • Added OpenUrlAction import
  • Replaced HideWindowAction with OpenUrlAction
  • Constructed the URL using the command's rtorr_description

12. The full project code can be viewed here:

ulauncher-vim repo

and the ulauncher extension here

References

  1. https://dev.to/brpaz/an-introduction-to-ulauncher-extension-development-1m69
  2. https://ext.ulauncher.io/-/github-ubuntupunk-ulauncher-vim
json Article's
30 articles in total
Favicon
How to Fetch URL Content, Set It into a Dictionary, and Extract Specific Keys in iOS Shortcuts
Favicon
Dynamic Routes in Astro (+load parameters from JSON)
Favicon
Effortlessly Host Static JSON Files with JSONsilo.com
Favicon
How to Implement Authentication in React Using JWT (JSON Web Tokens)
Favicon
Converting documents for LLM processing β€” A modern approach
Favicon
Import JSON Data in Astro (with Typescript)
Favicon
Devise not accepting JSON Token
Favicon
Integration for FormatJS/react-intl: Automated Translations with doloc
Favicon
β€œDefu” usage in unbuild source code.
Favicon
Converting documents for LLM processing β€” A modern approach
Favicon
How to crawl and parse JSON data with Python crawler
Favicon
JSON Visual Edit
Favicon
Develop a ulauncher extension with a command database
Favicon
Building a Smart Feedback Agent with Copilot Studio, Adaptive cards and Power Automate
Favicon
Simplifying JSON Validation with Ajv (Another JSON Validator)
Favicon
A Straightforward Guide to Building and Using aΒ JSON Database File
Favicon
AI prompt sample - a full chat content that demonstrates how to get a professional looking website in a few munities
Favicon
Fixing and Validating JSON with Ease: An In-Depth Guide
Favicon
Useful too to work with your JSON files - jq
Favicon
what is jq? a program for json files
Favicon
Code. Gleam. Extract fields from JSON
Favicon
Build an Application Without SQL Server Database (Avoiding RPrometheusedis, MongoDB, and )
Favicon
FAQ β€” Bloomer Mock Data Generator
Favicon
My React Journey: Day 18
Favicon
Working with JSON in MySQL
Favicon
JSON for Biggners
Favicon
angular and json
Favicon
iter.json: A Powerful and Efficient Way to Iterate and Manipulate JSON in Go
Favicon
This unknown Currency API is served over 50 Billion times a month !
Favicon
Common Data Formats in JavaScript: A Comprehensive Guide With Examples

Featured ones: