Skip to content

Commit

Permalink
feat: ibhagwan/fzf-lua support (#150)
Browse files Browse the repository at this point in the history
* feat: `ibhagwan/fzf-lua` support

* feat(picker): close buffer

* fix: telescope lang picker

* feat: adjust picker sizes

* docs: `picker.provider` config

* feat: picker provider resolver
  • Loading branch information
kawre authored Dec 12, 2024
1 parent 9813653 commit 74c1ff5
Show file tree
Hide file tree
Showing 17 changed files with 669 additions and 425 deletions.
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1

- [Neovim] >= 0.9.0

- [telescope.nvim]
- [telescope.nvim] or [fzf-lua]

- [plenary.nvim]

- [nui.nvim]

Expand All @@ -43,15 +45,12 @@ https://github.com/kawre/leetcode.nvim/assets/69250723/aee6584c-e099-4409-b114-1
```lua
{
"kawre/leetcode.nvim",
build = ":TSUpdate html",
build = ":TSUpdate html", -- if you have `nvim-treesitter` installed
dependencies = {
"nvim-telescope/telescope.nvim",
"nvim-lua/plenary.nvim", -- required by telescope
-- "ibhagwan/fzf-lua",
"nvim-lua/plenary.nvim",
"MunifTanjim/nui.nvim",

-- optional
"nvim-treesitter/nvim-treesitter",
"nvim-tree/nvim-web-devicons",
},
opts = {
-- configuration goes here
Expand Down Expand Up @@ -128,6 +127,9 @@ To see full configuration types see [template.lua](./lua/leetcode/config/templat
show_stats = true, ---@type boolean
},

---@type lc.picker
picker = { provider = nil },

hooks = {
---@type fun()[]
["enter"] = {},
Expand Down Expand Up @@ -275,6 +277,17 @@ injector = { ---@type table<lc.lang, lc.inject>
}
```

### picker

Supported picker providers are `telescope` and `fzf-lua`.
When provider is `nil`, [leetcode.nvim] will first try to use `fzf-lua`,
if not found it will fallback to `telescope`.

```lua
---@type lc.picker
picker = { provider = nil },
```

### hooks

List of functions that get executed on specified event
Expand Down Expand Up @@ -493,4 +506,6 @@ You can then exit [leetcode.nvim] using `:Leet exit` command
[nvim-treesitter]: https://github.com/nvim-treesitter/nvim-treesitter
[nvim-web-devicons]: https://github.com/nvim-tree/nvim-web-devicons
[telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim
[fzf-lua]: https://github.com/ibhagwan/fzf-lua
[tree-sitter-html]: https://github.com/tree-sitter/tree-sitter-html
[plenary.nvim]: https://github.com/nvim-lua/plenary.nvim
5 changes: 3 additions & 2 deletions lua/leetcode-ui/question.lua
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,9 @@ function Question:mount()
local msg = ("Snippet for `%s` not found. Select a different language"):format(self.lang)
log.warn(msg)

require("leetcode.pickers.language").pick_lang(self, function(snippet)
self.lang = snippet.t.slug
local picker = require("leetcode.picker")
picker.language(self, function(slug)
self.lang = slug
self:handle_mount()
end)
end
Expand Down
9 changes: 6 additions & 3 deletions lua/leetcode/command/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ function cmd.problems(options)
require("leetcode.utils").auth_guard()

local p = require("leetcode.cache.problemlist").get()
require("leetcode.pickers.question").pick(p, options)
local picker = require("leetcode.picker")
picker.question(p, options)
end

---@param cb? function
Expand Down Expand Up @@ -219,14 +220,16 @@ function cmd.start_user_session() --
end

function cmd.question_tabs()
require("leetcode.pickers.question-tabs").pick()
local picker = require("leetcode.picker")
picker.tabs()
end

function cmd.change_lang()
local utils = require("leetcode.utils")
local q = utils.curr_question()
if q then
require("leetcode.pickers.language").pick(q)
local picker = require("leetcode.picker")
picker.language(q)
end
end

Expand Down
5 changes: 5 additions & 0 deletions lua/leetcode/config/template.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

---@alias lc.storage table<"cache"|"home", string>

---@alias lc.picker { provider?: "fzf-lua" | "telescope" }

---@class lc.UserConfig
local M = {
---@type string
Expand Down Expand Up @@ -101,6 +103,9 @@ local M = {
show_stats = true, ---@type boolean
},

---@type lc.picker
picker = { provider = nil },

hooks = {
---@type fun()[]
["enter"] = {},
Expand Down
93 changes: 93 additions & 0 deletions lua/leetcode/picker/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
local log = require("leetcode.logger")
local config = require("leetcode.config")

---@return "fzf" | "telescope"
local function resolve_provider()
---@type string
local provider = config.user.picker.provider

if provider == nil then
local fzf_ok = pcall(require, "fzf-lua")
if fzf_ok then
return "fzf"
end
local telescope_ok = pcall(require, "telescope")
if telescope_ok then
return "telescope"
end
error("no supported picker provider found")
else
local provider_ok = pcall(require, provider)
assert(provider_ok, ("specified picker provider not found: `%s`"):format(provider))
return provider == "fzf-lua" and "fzf" or provider
end
end

---@class leet.Picker
local P = {}
P.provider = resolve_provider()

function P.hl_to_ansi(hl_group)
local color = vim.api.nvim_get_hl(0, { name = hl_group })
if color and color.fg then
return string.format(
"\x1b[38;2;%d;%d;%dm",
bit.rshift(color.fg, 16),
bit.band(bit.rshift(color.fg, 8), 0xFF),
bit.band(color.fg, 0xFF)
)
end
return ""
end

function P.apply_hl(text, hl_group)
if not hl_group then
return text
end
return P.hl_to_ansi(hl_group) .. text .. "\x1b[0m"
end

function P.normalize(items)
return vim.tbl_map(function(item)
return table.concat(
vim.tbl_map(function(col)
if type(col) == "table" then
return P.apply_hl(col[1], col[2])
else
return col
end
end, item.entry),
" "
)
end, items)
end

function P.pick(path, ...)
local rpath = table.concat({ "leetcode.picker", path, P.provider }, ".")
return require(rpath)(...)
end

function P.language(...)
P.pick("language", ...)
end

function P.question(...)
P.pick("question", ...)
end

function P.tabs()
local utils = require("leetcode.utils")
local tabs = utils.question_tabs()

if vim.tbl_isempty(tabs) then
return log.warn("No questions opened")
end

P.pick("tabs", tabs)
end

function P.hidden_field(text, deli)
return text:match(("([^%s]+)$"):format(deli))
end

return P
35 changes: 35 additions & 0 deletions lua/leetcode/picker/language/fzf.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
local fzf = require("fzf-lua")
local t = require("leetcode.translator")
local language_picker = require("leetcode.picker.language")
local Picker = require("leetcode.picker")

local deli = "\t"

return function(question, cb)
local items = language_picker.items(question.q.code_snippets)

for i, item in ipairs(items) do
local md = vim.inspect({ slug = item.value.t.slug, lang = item.value.t.lang })
:gsub("\n", "")
items[i] = table.concat({ Picker.normalize({ item })[1], md }, deli)
end

fzf.fzf_exec(items, {
prompt = t("Available Languages") .. "> ",
winopts = {
win_height = language_picker.height,
win_width = language_picker.width,
},
fzf_opts = {
["--delimiter"] = deli,
["--nth"] = "1",
["--with-nth"] = "1",
},
actions = {
["default"] = function(selected)
local md = Picker.hidden_field(selected[1], deli)
language_picker.select(load("return " .. md)(), question, cb)
end,
},
})
end
64 changes: 64 additions & 0 deletions lua/leetcode/picker/language/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
local log = require("leetcode.logger")
local t = require("leetcode.translator")
local config = require("leetcode.config")
local utils = require("leetcode.utils")

local L = {}

L.width = 80
L.height = 15

---@param snippet lc.QuestionCodeSnippet
local function dislay_icon(snippet)
local hl = "leetcode_lang_" .. snippet.t.slug
vim.api.nvim_set_hl(0, hl, { fg = snippet.t.color })

return { snippet.t.icon, hl }
end

---@param snippet lc.QuestionCodeSnippet
local function display_lang(snippet)
return { snippet.lang }
end

function L.entry(item)
return {
dislay_icon(item),
display_lang(item),
}
end

---@param item lc.QuestionCodeSnippet
function L.ordinal(item)
return ("%s %s"):format(item.t.lang, item.t.slug)
end

function L.items(content)
return vim.tbl_map(function(item)
---@type lc.language
item.t = utils.get_lang(item.lang_slug)
if not item.t then
return
end
return { entry = L.entry(item), value = item }
end, content)
end

function L.select(selection, question, cb, close)
if question.lang == selection.slug then
return log.warn(("%s: %s"):format(t("Language already set to"), selection.lang))
end

config.lang = selection.slug
if close then
close()
end

if cb then
cb(selection.slug)
else
question:change_lang(selection.slug)
end
end

return L
67 changes: 67 additions & 0 deletions lua/leetcode/picker/language/telescope.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
local log = require("leetcode.logger")
local t = require("leetcode.translator")

local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local config = require("leetcode.config")

local entry_display = require("telescope.pickers.entry_display")
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local language_picker = require("leetcode.picker.language")

local displayer = entry_display.create({
separator = " ",
items = {
{ width = 1 },
{ remaining = true },
},
})

local function entry_maker(item)
return {
value = item.value,
display = function()
return displayer(item.entry)
end,
ordinal = language_picker.ordinal(item.value),
}
end

local opts = require("telescope.themes").get_dropdown({
layout_config = {
width = language_picker.width,
height = language_picker.height,
},
})

---@param question lc.ui.Question
return function(question, cb)
local items = language_picker.items(question.q.code_snippets)

pickers
.new(opts, {
prompt_title = t("Available Languages"),
finder = finders.new_table({
results = items,
entry_maker = entry_maker,
}),
sorter = conf.generic_sorter(opts),
attach_mappings = function(prompt_bufnr)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
if not selection then
log.warn("No selection")
return
end
language_picker.select(selection.value.t, question, cb, function()
actions.close(prompt_bufnr)
end)
end)

return true
end,
})
:find()
end
Loading

0 comments on commit 74c1ff5

Please # to comment.