Skip to content
Merged
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ return {

-- Dependencies are lazy-loaded by default unless specified otherwise.
dependencies = {
{ 'akinsho/toggleterm.nvim' },
{ 'nvim-telescope/telescope.nvim' },
{ 'nvim-telescope/telescope-ui-select.nvim' },
{ 'nvim-lua/plenary.nvim' },
{ 'folke/which-key.nvim' },
{ 'nvim-treesitter/nvim-treesitter' }

-- install picker based on your likeing
{ 'akinsho/toggleterm.nvim' },
},
}
```
Expand All @@ -53,6 +55,7 @@ return {
vim.g.pioConfig ={
lsp = 'clangd', -- value: clangd | ccls
clangd_source = 'ccls', -- value: ccls | compiledb, For detailed explation check :help platformio-clangd_source
picker_backend = 'auto', -- value: auto | telescope | ui_select, default: auto, check :help platformio-picker
menu_key = '<leader>\\', -- replace this menu key to your convenience
debug = false -- enable debug messages
}
Expand Down
26 changes: 26 additions & 0 deletions doc/platformio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,30 @@ clangd_source *platformio-clangd_source
Try both methods and choose the one that works best for your setup. I am
also open to suggestions to further improve clangd support.

picker_backend *platformio-picker*
The plugin uses a picker abstraction for interactive selection flows used
by `:Pioinit` and `:Piolib`. This keeps command logic separate from UI so
different picker UIs can be used without changing command behavior.

Set this in `setup()`:
>lua
require('platformio').setup({
picker_backend = 'auto',
})
<

Allowed values:
- `auto`
Default. Tries Telescope backend first. If Telescope is unavailable,
falls back to `vim.ui.select` backend.

- `telescope`
Force Telescope picker backend. If Telescope backend cannot be loaded,
plugin falls back to `vim.ui.select` as a safety fallback.

- `ui_select`
Force `vim.ui.select` backend. This works with Neovim built-in
`vim.ui.select` or any plugin that overrides it (for example,
telescope-ui-select, dressing.nvim, snacks picker adapters, etc.).

vim:tw=78:sw=4:ts=8:ft=help:norl:noet:
1 change: 1 addition & 0 deletions doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
PlatformIO platformio.txt /*PlatformIO*
platformio-clangd_source platformio.txt /*platformio-clangd_source*
platformio-configuration platformio.txt /*platformio-configuration*
platformio-picker platformio.txt /*platformio-picker*
platformio-usage platformio.txt /*platformio-usage*
platformio.lua platformio.txt /*platformio.lua*
15 changes: 15 additions & 0 deletions lua/platformio/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ M.config = {
menu_name = 'PlatformIO',
debug = false,
clangd_source = 'ccls',
picker_backend = 'auto',

menu_bindings = {
{ node = 'item', desc = '[L]ist terminals', shortcut = 'l', command = 'PioTermList' },
Expand Down Expand Up @@ -160,6 +161,7 @@ function M.setup(user_config)
menu_bindings = true,
debug = true,
clangd_source = true,
picker_backend = true,
}
local err = false
for key, value in pairs(user_config or {}) do
Expand Down Expand Up @@ -187,6 +189,19 @@ function M.setup(user_config)
user_config.clangd_source = M.config.clangd_source
end
end
if
user_config.picker_backend
and user_config.picker_backend ~= 'auto'
and user_config.picker_backend ~= 'telescope'
and user_config.picker_backend ~= 'ui_select'
then
vim.api.nvim_echo(
{ { 'Invalid picker backend {allowed "auto", "telescope" or "ui_select"} (default "' .. M.config.picker_backend .. '" will be used)', 'ErrorMsg' } },
true,
{}
)
user_config.picker_backend = M.config.picker_backend
end

if not err then -- if no error, merge user_config to M.config
if user_config.menu_bindings then
Expand Down
63 changes: 63 additions & 0 deletions lua/platformio/pickers/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
local M = {}

local config = require('platformio').config

local function load_backend(name)
local ok, backend = pcall(require, 'platformio.pickers.' .. name)
if ok then
return backend
end
return nil
end

local function get_backend()
local backend_name = config.picker_backend or 'auto'

if backend_name == 'telescope' then
return load_backend('telescope') or load_backend('ui_select')
end

if backend_name == 'ui_select' then
return load_backend('ui_select')
end

return load_backend('telescope') or load_backend('ui_select')
end

function M.pick_board(boards, on_select)
local backend = get_backend()
if not backend then
vim.notify('No picker backend available for PlatformIO.', vim.log.levels.ERROR)
return
end
backend.pick_board(boards, on_select)
end

function M.pick_framework(frameworks, on_select)
local backend = get_backend()
if not backend then
vim.notify('No picker backend available for PlatformIO.', vim.log.levels.ERROR)
return
end
backend.pick_framework(frameworks, on_select)
end

function M.pick_library(libraries, on_select)
local backend = get_backend()
if not backend then
vim.notify('No picker backend available for PlatformIO.', vim.log.levels.ERROR)
return
end
backend.pick_library(libraries, on_select)
end

function M.pick_terminal(terminals, on_select)
local backend = get_backend()
if not backend then
vim.notify('No picker backend available for PlatformIO.', vim.log.levels.ERROR)
return
end
backend.pick_terminal(terminals, on_select)
end

return M
211 changes: 211 additions & 0 deletions lua/platformio/pickers/telescope.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
local M = {}

local pickers = require('telescope.pickers')
local finders = require('telescope.finders')
local telescope_conf = require('telescope.config').values
local actions = require('telescope.actions')
local action_state = require('telescope.actions.state')
local entry_display = require('telescope.pickers.entry_display')
local make_entry = require('telescope.make_entry')
local previewers = require('telescope.previewers')
local utils = require('platformio.utils')

local function boardentry_maker(opts)
local displayer = entry_display.create({
separator = '▏',
items = {
{ width = 35 },
{ width = 20 },
{ width = 15 },
},
})

local make_display = function(entry)
return displayer({
entry.value.name,
entry.value.vendor,
entry.value.platform,
})
end

return function(entry)
return make_entry.set_default_entry_mt({
value = {
id = entry.id,
name = entry.name,
vendor = entry.vendor,
platform = entry.platform,
data = entry,
},
ordinal = entry.name .. ' ' .. entry.vendor .. ' ' .. entry.platform,
display = make_display,
}, opts)
end
end

local function libentry_maker(opts)
local displayer = entry_display.create({
separator = '▏',
items = {
{ width = 20 },
{ width = 20 },
{ remaining = true },
},
})

local make_display = function(entry)
return displayer({
entry.value.name,
entry.value.owner,
entry.value.description,
})
end

return function(entry)
return make_entry.set_default_entry_mt({
value = {
name = entry.name,
owner = entry.owner and entry.owner.username or '',
description = entry.description,
data = entry,
},
ordinal = (entry.name or '') .. ' ' .. (entry.owner and entry.owner.username or '') .. ' ' .. (entry.description or ''),
display = make_display,
}, opts)
end
end

function M.pick_framework(frameworks, on_select)
local opts = {}
pickers
.new(opts, {
prompt_title = 'frameworks',
finder = finders.new_table({
results = frameworks,
}),
attach_mappings = function(prompt_bufnr, _)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
if selection then
on_select(selection[1] or selection.value)
end
end)
return true
end,
sorter = telescope_conf.generic_sorter(opts),
})
:find()
end

function M.pick_board(boards, on_select)
local opts = {}
pickers
.new(opts, {
prompt_title = 'Boards',
finder = finders.new_table({
results = boards,
entry_maker = opts.entry_maker or boardentry_maker(opts),
}),
attach_mappings = function(prompt_bufnr, _)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
if selection and selection.value then
on_select(selection.value.data)
end
end)
return true
end,
previewer = previewers.new_buffer_previewer({
title = 'Board Info',
define_preview = function(self, entry, _)
local json = utils.strsplit(vim.inspect(entry.value.data), '\n')
local bufnr = self.state.bufnr
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, json)
vim.api.nvim_set_option_value('filetype', 'lua', { buf = bufnr })
vim.defer_fn(function()
local win = self.state.winid
vim.api.nvim_set_option_value('wrap', true, { scope = 'local', win = win })
vim.api.nvim_set_option_value('linebreak', true, { scope = 'local', win = win })
vim.api.nvim_set_option_value('wrapmargin', 2, { buf = bufnr })
end, 0)
end,
}),
sorter = telescope_conf.generic_sorter(opts),
})
:find()
end

function M.pick_library(libraries, on_select)
local opts = {}
pickers
.new(opts, {
prompt_title = 'Libraries',
finder = finders.new_table({
results = libraries,
entry_maker = opts.entry_maker or libentry_maker(opts),
}),
attach_mappings = function(prompt_bufnr, _)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
if selection and selection.value then
on_select(selection.value.data)
end
end)
return true
end,
previewer = previewers.new_buffer_previewer({
title = 'Package Info',
define_preview = function(self, entry, _)
local json = utils.strsplit(vim.inspect(entry.value.data), '\n')
local bufnr = self.state.bufnr
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, json)
vim.api.nvim_set_option_value('filetype', 'lua', { buf = bufnr })
vim.defer_fn(function()
local win = self.state.winid
vim.api.nvim_set_option_value('wrap', true, { scope = 'local', win = win })
vim.api.nvim_set_option_value('linebreak', true, { scope = 'local', win = win })
vim.api.nvim_set_option_value('wrapmargin', 2, { buf = bufnr })
end, 0)
end,
}),
sorter = telescope_conf.generic_sorter(opts),
})
:find()
end

function M.pick_terminal(terminals, on_select)
local opts = {}
pickers
.new(opts, {
prompt_title = 'PIO terminals',
finder = finders.new_table({
results = terminals,
entry_maker = function(entry)
local is_hidden = vim.api.nvim_buf_is_loaded(entry.term.bufnr) and (vim.fn.bufwinid(entry.term.bufnr) == -1)
local label = string.format('%d:%s (hidden: %s)', entry.term.id, entry.termtype, tostring(is_hidden))
return {
value = entry,
display = label,
ordinal = label,
}
end,
}),
attach_mappings = function(prompt_bufnr, _)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
if selection and selection.value then
on_select(selection.value)
end
end)
return true
end,
sorter = telescope_conf.generic_sorter(opts),
})
:find()
end

return M
Loading