Skip to content

feat: ibhagwan/fzf-lua support #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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