From 32963dcca42f20fda1f1cd954a4f2bea3c2131ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=B6sl?= Date: Wed, 13 Mar 2024 12:56:19 +0100 Subject: [PATCH] feat: user defined handlers (#49) --- README.md | 10 ++++++ lua/gx/handler.lua | 59 +++++++++++++++++++++-------------- lua/gx/init.lua | 30 ++++++++++-------- test/spec/gx/handler_spec.lua | 41 +++++++++++++++++++----- 4 files changed, 95 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index af325c0..5630c06 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ * dependencies from `package.json` (e.g. line `"express": "^4.18.2",` in the `package.json` opens `https://www.npmjs.com/package/express`) * formulae and casks from `Brewfile` (e.g. line `brew "neovim"` in the `Brewfile` opens `https://formulae.brew.sh/formula/neovim`) * if there is no url found under the cursor, the word/selection is automatically searched on the web +* supports user defined handlers to extend the functionality * support for macOS, Linux and Windows * more to come (jira issues, ..) @@ -50,6 +51,15 @@ require("lazy").setup({ brewfile = true, -- open Homebrew formulaes and casks package_json = true, -- open dependencies from package.json search = true, -- search the web/selection on the web if nothing else is found + jira = { -- custom handler to open Jira tickets (these have higher precedence than builtin handlers) + handle = function(mode, line, _) + local ticket = require("gx.helper").find(line, mode, "(%u+-%d+)") + if ticket and #ticket < 20 then + return "http://jira.company.com/browse/" .. ticket + end + end, + }, + }, }, handler_options = { search_engine = "google", -- you can select between google, bing, duckduckgo, and ecosia diff --git a/lua/gx/handler.lua b/lua/gx/handler.lua index c15ca49..04a4b15 100644 --- a/lua/gx/handler.lua +++ b/lua/gx/handler.lua @@ -12,46 +12,57 @@ local M = {} local function add_handler(handlers, handler, active) if - not active + active == false or not helper.check_filetype(handler.filetype) or not helper.check_filename(handler.filename) then return end - table.insert(handlers, handler) + handlers[#handlers + 1] = handler end --- handler function -function M.get_url(mode, line, activated_handlers, handler_options) - local url - local handlers = {} - local tkeys = {} +---@param handlers { [string]: (boolean | GxHandler)[] } +---@return GxHandler[] +local function resolve_handlers(handlers) + local resolved = {} + local exists = {} + + for name, config in pairs(handlers) do + if type(config) == "table" then + add_handler(resolved, config, true) + exists[name] = true + end + end -- ### add here new handlers - add_handler(handlers, brewfile_handler, activated_handlers.brewfile) - add_handler(handlers, package_json_handler, activated_handlers.package_json) - add_handler(handlers, plugin_handler, activated_handlers.plugin) - add_handler(handlers, github_handler, activated_handlers.github) - add_handler(handlers, commit_handler, activated_handlers.github) - add_handler(handlers, markdown_handler, true) - add_handler(handlers, url_handler, true) - add_handler(handlers, search_handler, activated_handlers.search) + add_handler(resolved, brewfile_handler, handlers.brewfile and exists.brewfile == nil) + add_handler(resolved, package_json_handler, handlers.package_json and exists.package_json == nil) + add_handler(resolved, plugin_handler, handlers.plugin and exists.plugin == nil) + add_handler(resolved, github_handler, handlers.github and exists.github == nil) + add_handler(resolved, commit_handler, handlers.commit and exists.commit == nil) + add_handler(resolved, markdown_handler, handlers.markdown and exists.markdown == nil) + add_handler(resolved, url_handler, handlers.url and exists.url == nil) + add_handler(resolved, search_handler, handlers.search and exists.search == nil) -- ### - for k in pairs(handlers) do - table.insert(tkeys, k) - end - table.sort(tkeys) + return resolved +end + +-- handler function +---@param mode string +---@param line string +---@param configured_handlers { [string]: (boolean | GxHandler)[] } +---@return string | nil +function M.get_url(mode, line, configured_handlers, handler_options) + local handlers = resolve_handlers(configured_handlers) - for _, k in ipairs(tkeys) do - url = handlers[k].handle(mode, line, handler_options) + for _, handler in ipairs(handlers) do + local url = handler.handle(mode, line, handler_options) if url then - break + return url end end - - return url end return M diff --git a/lua/gx/init.lua b/lua/gx/init.lua index 2146d98..0fedf68 100644 --- a/lua/gx/init.lua +++ b/lua/gx/init.lua @@ -4,6 +4,20 @@ local sysname = vim.loop.os_uname().sysname local M = {} +---@class GxHandlerOptions +---@field search_engine string + +---@class GxHandler +---@field filetypes string[] | nil +---@field filename string | nil +---@field handle fun(mode: string, line: string, handler_options: GxHandlerOptions | nil) + +---@class GxOptions +---@field open_browser_app string +---@field open_browser_args string[] +---@field handlers (boolean | GxHandler)[] +---@field handler_options GxHandlerOptions | nil + -- search for url with handler function M.open(mode, line) if not line then @@ -52,23 +66,12 @@ end local function with_defaults(options) options = options or {} - options.handlers = options.handlers or {} options.handler_options = options.handler_options or {} return { open_browser_app = options.open_browser_app or get_open_browser_app(), open_browser_args = get_open_browser_args(options.open_browser_args or {}), - handlers = { - brewfile = helper.ternary(options.handlers.brewfile ~= nil, options.handlers.brewfile, true), - plugin = helper.ternary(options.handlers.plugin ~= nil, options.handlers.plugin, true), - github = helper.ternary(options.handlers.github ~= nil, options.handlers.github, true), - package_json = helper.ternary( - options.handlers.package_json ~= nil, - options.handlers.package_json, - true - ), - search = helper.ternary(options.handlers.search ~= nil, options.handlers.search, true), - }, + handlers = options.handlers or {}, handler_options = { search_engine = options.handler_options.search_engine or "google", }, @@ -93,12 +96,13 @@ local function bind_command() end, { nargs = "?", range = 1 }) end --- setup function +---@param options GxOptions function M.setup(options) M.options = with_defaults(options) bind_command() end +---@type GxOptions M.options = nil return M diff --git a/test/spec/gx/handler_spec.lua b/test/spec/gx/handler_spec.lua index 8926a74..9582388 100644 --- a/test/spec/gx/handler_spec.lua +++ b/test/spec/gx/handler_spec.lua @@ -3,14 +3,16 @@ local handler = require("gx.handler") local stub = require("luassert.stub") describe("test handler", function() - local activated_handlers = { - plugin = false, - github = false, - package_json = false, - } + local activated_handlers before_each(function() before_mock_filetype = vim.bo.filetype + activated_handlers = { + plugin = false, + github = false, + package_json = false, + search = false, + } end) after_each(function() @@ -26,8 +28,8 @@ describe("test handler", function() "https://github.com", handler.get_url("v", "https://github.com", activated_handlers) ) - assert.equals(nil, handler.get_url("v", '"example_user/example_plugin"', activated_handlers)) - assert.equals(nil, handler.get_url("v", "Fixes #22", activated_handlers)) + assert.is.Nil(handler.get_url("v", '"example_user/example_plugin"', activated_handlers)) + assert.is.Nil(handler.get_url("v", "Fixes #22", activated_handlers)) end) it("plugin handler on", function() @@ -48,6 +50,7 @@ describe("test handler", function() end) it("plugin handler on and filetype vim", function() + activated_handlers.plugin = true activated_handlers.github = true -- mock filetype @@ -75,7 +78,7 @@ describe("test handler", function() "https://github.com", handler.get_url("v", "https://github.com", activated_handlers) ) - assert.equals(nil, handler.get_url("v", '"example_user/example_plugin"', activated_handlers)) + assert.is.Nil(handler.get_url("v", '"example_user/example_plugin"', activated_handlers)) end) it("github handler on", function() @@ -111,4 +114,26 @@ describe("test handler", function() helper.get_filename:revert() end) + + it("user defined handler has precedence", function() + activated_handlers.commit = true + activated_handlers.custom = { + handle = function() + return "https://from.user.handler" + end, + } + + assert.equals("https://from.user.handler", handler.get_url("v", "1a2b3c4", activated_handlers)) + end) + + it("user defined handler instead of builtin handler", function() + activated_handlers.commit = true + activated_handlers.commit = { + handle = function() + return "https://from.user.handler" + end, + } + + assert.equals("https://from.user.handler", handler.get_url("v", "1a2b3c4", activated_handlers)) + end) end)