Skip to content

Instantly share code, notes, and snippets.

@brainlet-ali
Created April 11, 2026 14:03
Show Gist options
  • Select an option

  • Save brainlet-ali/1a5d10d8118fc4090f281ed5df7f9596 to your computer and use it in GitHub Desktop.

Select an option

Save brainlet-ali/1a5d10d8118fc4090f281ed5df7f9596 to your computer and use it in GitHub Desktop.
PhpStorm to LazyVim — full config for every replaced feature (dap, test runner, REST client, database, git, Laravel navigation, LSP)
return {
{
"Weissle/persistent-breakpoints.nvim",
lazy = true, -- Only load when DAP is used
opts = {
load_breakpoints_event = nil, -- Don't auto-load on BufReadPost
},
},
{
"mfussenegger/nvim-dap",
lazy = true,
dependencies = {
{ "rcarriga/nvim-dap-ui" },
{ "theHamsta/nvim-dap-virtual-text" },
{ "nvim-telescope/telescope-dap.nvim" },
{ "folke/which-key.nvim" },
{ "jay-babu/mason-nvim-dap.nvim" },
},
keys = {
{ "<leader>dB", function() require("persistent-breakpoints.api").set_conditional_breakpoint() end, desc = "Breakpoint Condition" },
{ "<leader>db", function() require("persistent-breakpoints.api").toggle_breakpoint() end, desc = "Toggle Breakpoint" },
{ "<leader>dc", function() require("dap").continue() end, desc = "Continue" },
{ "<leader>dd", function() require("dap").continue() end, desc = "Continue (F5)" },
{ "<leader>dC", function() require("dap").run_to_cursor() end, desc = "Run to Cursor" },
{ "<leader>dg", function() require("dap").goto_() end, desc = "Go to line (no execute)" },
{ "<leader>di", function() require("dap").step_into() end, desc = "Step Into" },
{ "<leader>dj", function() require("dap").down() end, desc = "Down" },
{ "<leader>dk", function() require("dap").up() end, desc = "Up" },
{ "<leader>dl", function() require("dap").run_last() end, desc = "Run Last" },
{ "<leader>do", function() require("dap").step_out() end, desc = "Step Out" },
{ "<leader>dO", function() require("dap").step_over() end, desc = "Step Over" },
{ "<leader>ds", function() require("dap").step_over() end, desc = "Step Over" },
{ "<leader>dp", function() require("dap").pause() end, desc = "Pause" },
{ "<leader>de", function()
local widgets = require('dap.ui.widgets')
local sidebar = widgets.sidebar(widgets.frames)
sidebar.open()
end, desc = "Show Call Stack" },
{ "<leader>dE", function()
local expression = vim.fn.input("Expression: ")
require('dap.ui.widgets').hover(expression)
end, desc = "Evaluate Expression" },
{ "<leader>dr", function() require("dap").repl.toggle() end, desc = "Toggle REPL" },
{ "<leader>dt", function() require("dap").terminate() end, desc = "Terminate" },
{ "<leader>dw", function() require('dap.ui.widgets').hover() end, desc = "Hover at Point" },
{ "<leader>dW", function()
local widgets = require('dap.ui.widgets')
local sidebar = widgets.sidebar(widgets.scopes)
sidebar.open()
end, desc = "Show Variables/Watches" },
{ "<leader>du", function()
require("dapui").close({ layout = 2 })
require("dapui").close({ layout = 3 })
require("dapui").toggle({ layout = 1 })
end, desc = "Toggle Minimal (Scopes/Breakpoints)" },
{ "<leader>d1", function()
require("dapui").close({ layout = 1 })
require("dapui").toggle({ layout = 2 })
require("dapui").toggle({ layout = 3 })
end, desc = "Toggle Full DAP UI" },
},
config = function()
local dap = require("dap")
local home = os.getenv('HOME')
-- Default PHP adapter configuration
dap.adapters.php = {
type = 'executable',
command = 'node',
args = { home .. '/.local/share/nvim/mason/packages/php-debug-adapter/extension/out/phpDebug.js' }
}
-- Load project-specific DAP configuration
local function load_project_dap_config()
local project_config_path = vim.fn.getcwd() .. "/.nvim/dap.lua"
if vim.fn.filereadable(project_config_path) == 1 then
-- Load and execute the project's DAP configuration
local ok, project_config = pcall(dofile, project_config_path)
if ok and type(project_config) == "function" then
-- Call the function with dap as argument
project_config(dap)
elseif ok and type(project_config) == "table" then
-- Merge configurations
for lang, configs in pairs(project_config) do
dap.configurations[lang] = configs
end
end
else
-- Default configuration if no project-specific config exists
dap.configurations.php = {
{
type = 'php',
request = 'launch',
name = 'Listen for Xdebug',
port = 9003,
pathMappings = {
["/var/www/html"] = vim.fn.getcwd()
},
hostname = "0.0.0.0",
log = true,
xdebugSettings = {
max_children = 100,
max_data = 1024,
max_depth = 5,
},
}
}
end
end
-- Load configurations on startup and when changing directories
load_project_dap_config()
-- Auto-reload when changing directories
vim.api.nvim_create_autocmd("DirChanged", {
callback = function()
load_project_dap_config()
end,
})
-- UI customization with better icons
vim.fn.sign_define('DapBreakpoint', { text = '●', texthl = 'DiagnosticError', linehl = '', numhl = '' })
vim.fn.sign_define('DapBreakpointCondition', { text = '◆', texthl = 'DiagnosticError', linehl = '', numhl = '' })
vim.fn.sign_define('DapBreakpointRejected', { text = '○', texthl = 'DiagnosticHint', linehl = '', numhl = '' })
vim.fn.sign_define('DapLogPoint', { text = '◉', texthl = 'DiagnosticInfo', linehl = '', numhl = '' })
vim.fn.sign_define('DapStopped', { text = '▶', texthl = 'DiagnosticWarn', linehl = 'DapStoppedLine', numhl = 'DapStoppedLine' })
-- Create a highlight group for the stopped line (PHPStorm-like)
vim.api.nvim_set_hl(0, 'DapStoppedLine', { bg = '#CCDDFF', default = true })
-- Focus terminal when breakpoint is hit (macOS)
dap.listeners.after.event_stopped["focus_nvim"] = function()
vim.fn.system([[osascript -e 'tell application "Ghostty" to activate']])
end
end,
},
{
"rcarriga/nvim-dap-ui",
lazy = true,
dependencies = {
"nvim-neotest/nvim-nio",
},
opts = {
icons = { expanded = "▾", collapsed = "▸", current_frame = "▸" },
mappings = {
expand = { "<CR>", "<2-LeftMouse>" },
open = "o",
remove = "d",
edit = "e",
repl = "r",
toggle = "t",
},
layouts = {
{
-- Layout 1: Minimal (scopes + breakpoints only)
elements = {
{ id = "scopes", size = 0.6 },
{ id = "breakpoints", size = 0.4 },
},
size = 0.30,
position = "right",
},
{
-- Layout 2: Full sidebar
elements = {
{ id = "scopes", size = 0.33 },
{ id = "breakpoints", size = 0.17 },
{ id = "stacks", size = 0.25 },
{ id = "watches", size = 0.25 },
},
size = 0.33,
position = "right",
},
{
-- Layout 3: Bottom console/repl
elements = {
{ id = "repl", size = 0.45 },
{ id = "console", size = 0.55 },
},
size = 0.27,
position = "bottom",
},
},
floating = {
max_height = 0.9,
max_width = 0.5,
border = "single",
mappings = {
close = { "q", "<Esc>" },
},
},
},
config = function(_, opts)
local dap = require("dap")
local dapui = require("dapui")
dapui.setup(opts)
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open({ layout = 1 })
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close()
end
end,
},
{
"theHamsta/nvim-dap-virtual-text",
lazy = true,
opts = {},
},
{
"folke/which-key.nvim",
optional = true,
opts = {
spec = {
["<leader>d"] = { name = "+debug" },
},
},
},
}
return {
"vim-test/vim-test",
dependencies = {
"preservim/vimux",
"christoomey/vim-tmux-navigator",
},
keys = {
{ "<leader>Tt", "<cmd>TestNearest<cr>", desc = "Run nearest test" },
{ "<leader>Tf", "<cmd>TestFile<cr>", desc = "Run test file" },
{ "<leader>Ts", "<cmd>TestSuite<cr>", desc = "Run test suite" },
{ "<leader>Tl", "<cmd>TestLast<cr>", desc = "Run last test" },
{ "<leader>Tv", "<cmd>TestVisit<cr>", desc = "Visit test file" },
},
config = function()
-- Load project-specific test config from .nvim/test.lua
local function load_project_test_config()
local config_path = vim.fn.getcwd() .. "/.nvim/test.lua"
if vim.fn.filereadable(config_path) == 1 then
local ok, config = pcall(dofile, config_path)
if ok and type(config) == "function" then
config(vim.g)
elseif ok and type(config) == "table" then
for key, value in pairs(config) do
vim.g[key] = value
end
end
end
-- No fallback - project must provide .nvim/test.lua
end
load_project_test_config()
-- Reload on directory change
vim.api.nvim_create_autocmd("DirChanged", {
callback = load_project_test_config,
})
-- Vimux defaults
vim.g.VimuxHeight = "30%"
vim.g.VimuxOrientation = "h"
vim.g.VimuxCloseOnExit = 0
vim.g.VimuxUseNearest = 0
vim.g.VimuxRunnerName = "nvim-test"
end,
}
return {
"mistweaverco/kulala.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
ft = "http",
keys = {
{
"<leader>rs",
function()
require("kulala").run()
end,
desc = "Send request",
mode = { "n", "v" },
},
{
"<leader>rr",
function()
require("kulala").replay()
end,
desc = "Replay last request",
},
{
"<leader>rc",
function()
require("kulala").copy()
end,
desc = "Copy as cURL",
},
{
"<leader>re",
function()
require("kulala").set_selected_env()
-- Switch to normal mode after picker opens
vim.schedule(function()
vim.cmd("stopinsert")
end)
end,
desc = "Switch environment",
},
{
"<leader>rb",
function()
require("kulala").scratchpad()
end,
desc = "Open scratchpad",
},
},
config = function()
-- Default configuration
local default_opts = {
-- Default environment and view settings
default_env = "local",
default_view = "headers_body",
environment_scope = "g", -- Use global scope for environment variables
vscode_rest_client_environmentvars = true, -- Enable VSCode REST client environment variables
-- Content type settings
contenttypes = {
["application/json"] = {
ft = "json",
formatter = { "jq", "." },
pathresolver = require("kulala.parser.jsonpath").parse,
},
},
-- Visual feedback settings
show_icons = "on_request",
icons = {
inlay = {
loading = "⏳",
done = "✅",
error = "❌",
},
lualine = "🐼",
},
-- Request settings
urlencode = "skipencoded",
show_variable_info_text = "float",
-- Scratchpad template
scratchpad_default_contents = {
"# @name example-request",
"GET {{host}}/{{api}}/users",
"Authorization: Bearer {{token}}",
"Content-Type: application/json",
"",
"{",
' "query": "example"',
"}",
},
-- Enable winbar for better navigation
winbar = true,
default_winbar_panes = { "body", "headers", "headers_body", "verbose" },
}
-- Load project-specific kulala configuration
local function load_project_kulala_config()
local project_config_path = vim.fn.getcwd() .. "/.nvim/kulala.lua"
if vim.fn.filereadable(project_config_path) == 1 then
-- Load and execute the project's kulala configuration
local ok, project_config = pcall(dofile, project_config_path)
if ok and type(project_config) == "function" then
-- Call the function with default_opts as argument for configuration
return project_config(default_opts)
elseif ok and type(project_config) == "table" then
-- Merge project config with default config
return vim.tbl_deep_extend("force", default_opts, project_config)
end
end
-- Return default configuration if no project-specific config exists
return default_opts
end
-- Get final configuration and setup kulala
local final_opts = load_project_kulala_config()
require("kulala").setup(final_opts)
-- Auto-reload when changing directories
vim.api.nvim_create_autocmd("DirChanged", {
callback = function()
local new_opts = load_project_kulala_config()
require("kulala").setup(new_opts)
end,
})
end,
}
return {
{
"tpope/vim-dadbod",
cmd = { "DBUI", "DBUIToggle", "DBUIFindBuffer", "DBUIRenameBuffer", "DBUILastQueryInfo" },
ft = { "sql", "mysql", "plsql" },
dependencies = {
"kristijanhusak/vim-dadbod-ui",
"kristijanhusak/vim-dadbod-completion",
"pbogut/vim-dadbod-ssh",
},
config = function()
-- Database connections
vim.g.db_ui_save_location = vim.fn.stdpath("config") .. require("plenary.path").path.sep .. "db_ui"
-- Load project-specific config from .nvim/dadbod.lua
local function load_project_config()
local config_path = vim.fn.getcwd() .. "/.nvim/dadbod.lua"
if vim.fn.filereadable(config_path) == 1 then
local ok, config = pcall(dofile, config_path)
if ok and type(config) == "function" then
config(vim.g)
elseif ok and type(config) == "table" then
if config.dbs then vim.g.dbs = config.dbs end
for k, v in pairs(config) do
if k ~= "dbs" then vim.g[k] = v end
end
end
end
end
load_project_config()
vim.api.nvim_create_autocmd("DirChanged", {
callback = load_project_config,
})
-- DBUI settings (global defaults)
vim.g.db_ui_use_nerd_fonts = 1
vim.g.db_ui_show_database_icon = 1
vim.g.db_ui_force_echo_messages = 1
vim.g.db_ui_win_position = "right"
vim.g.db_ui_winwidth = 40
-- Auto-completion for SQL
vim.api.nvim_create_autocmd("FileType", {
pattern = { "sql", "mysql", "plsql" },
callback = function()
-- Disable built-in SQL completion (causes sqlcomplete#DrillIntoTable error)
vim.bo.omnifunc = ""
local ok, cmp = pcall(require, "cmp")
if ok then
cmp.setup.buffer({
sources = {
{ name = "luasnip" },
{ name = "vim-dadbod-completion" },
{ name = "buffer" },
},
})
end
end,
})
-- Connection-specific background colors for safety
-- Define highlight groups for different environments (light theme)
vim.api.nvim_set_hl(0, "DbLocalBg", { bg = "NONE" }) -- default
vim.api.nvim_set_hl(0, "DbStagingBg", { bg = "#fff3e0" }) -- orange/peach for staging
vim.api.nvim_set_hl(0, "DbProdBg", { bg = "#ffebee" }) -- light red for prod (danger)
-- Apply background color based on connection name
local function get_db_name()
-- Try multiple sources for connection name
local db_name = vim.b.dbui_db_key_name
or vim.b.db_name
or vim.fn.expand("%:p"):match("db_ui/([^/]+)/")
or vim.fn.expand("%"):match("^([^-]+)%-") -- parse from buffer name like "STAGING4-query"
or ""
return string.upper(db_name)
end
local function apply_db_color()
local ft = vim.bo.filetype
if ft ~= "sql" and ft ~= "mysql" and ft ~= "plsql" and ft ~= "dbout" then
return
end
local db_name = get_db_name()
-- Check STAGING first (more specific)
if db_name:match("STAGING") then
vim.wo.winhighlight = "Normal:DbStagingBg"
elseif db_name:match("PROD") then
vim.wo.winhighlight = "Normal:DbProdBg"
else
vim.wo.winhighlight = ""
end
-- Store for dbout to inherit
if ft ~= "dbout" and db_name ~= "" then
vim.g.last_db_name = db_name
end
end
vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter", "FileType" }, {
pattern = "*",
callback = function()
local ft = vim.bo.filetype
local bufname = vim.fn.expand("%")
-- Only apply to SQL-related buffers
local is_sql = ft == "sql" or ft == "mysql" or ft == "plsql"
local is_dbout = ft == "dbout" or bufname:match("%.dbout$")
if not is_sql and not is_dbout then
-- Reset highlight for non-SQL buffers
vim.wo.winhighlight = ""
return
end
-- For dbout (result) buffers, use last known connection
if is_dbout then
local db_name = vim.g.last_db_name or ""
if db_name:match("STAGING") then
vim.wo.winhighlight = "Normal:DbStagingBg"
elseif db_name:match("PROD") then
vim.wo.winhighlight = "Normal:DbProdBg"
else
vim.wo.winhighlight = ""
end
return
end
apply_db_color()
end,
})
end,
keys = {
{ "<leader>;", "<cmd>DBUI<cr>", desc = "Database UI" },
{ "<leader>;u", "<cmd>DBUI<cr>", desc = "Open Database UI" },
{ "<leader>;t", "<cmd>DBUIToggle<cr>", desc = "Toggle Database UI" },
{ "<leader>;f", "<cmd>DBUIFindBuffer<cr>", desc = "Find Database Buffer" },
{ "<leader>;r", "<cmd>DBUIRenameBuffer<cr>", desc = "Rename Database Buffer" },
{ "<leader>;l", "<cmd>DBUILastQueryInfo<cr>", desc = "Last Query Info" },
{ "<leader>;a", "<cmd>DBUIAddConnection<cr>", desc = "Add Connection" },
{ "<leader>;x", "<Plug>(DBUI_ExecuteQuery)", desc = "Execute Query", ft = { "sql", "mysql", "plsql" } },
},
},
}
return {
{
"kdheepak/lazygit.nvim",
keys = {
{ "<leader>gg", "<cmd>LazyGit<cr>", desc = "LazyGit" },
},
},
{
"lewis6991/gitsigns.nvim",
keys = {
-- Find PR that introduced current line
{
"<leader>gP",
function()
local file = vim.fn.expand("%:p")
local line = vim.fn.line(".")
local blame = vim.fn.system(string.format("git blame -L %d,%d -l %s 2>/dev/null", line, line, vim.fn.shellescape(file)))
if blame == "" then
vim.notify("Could not get blame info", vim.log.levels.WARN)
return
end
local pr_num = blame:match("%(#(%d+)%)")
if pr_num then
vim.notify("PR #" .. pr_num, vim.log.levels.INFO)
vim.fn.system("gh pr view " .. pr_num .. " --web")
return
end
local sha = blame:match("(%x%x%x%x%x%x%x%x%x%x+)")
if sha and not sha:match("^0+$") then
local pr = vim.fn.system(string.format("gh api /repos/{owner}/{repo}/commits/%s/pulls --jq '.[0].number' 2>/dev/null", sha))
pr_num = pr:match("(%d+)")
if pr_num then
vim.notify("PR #" .. pr_num, vim.log.levels.INFO)
vim.fn.system("gh pr view " .. pr_num .. " --web")
else
vim.notify("No PR found for commit: " .. sha:sub(1, 8), vim.log.levels.WARN)
end
else
vim.notify("Line not committed yet", vim.log.levels.WARN)
end
end,
desc = "Find PR for line",
},
},
},
{
"nvim-telescope/telescope.nvim",
keys = {
-- Modified files since branch diverged
{ "<leader>gm", function()
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local previewers = require("telescope.previewers")
local base_branch = vim.fn.system("git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@'"):gsub("\n", "")
if base_branch == "" then
base_branch = vim.fn.system("git rev-parse --verify main 2>/dev/null && echo main || echo master"):gsub("\n", "")
end
local cmd = "git diff --name-only " .. base_branch .. "...HEAD"
local files = vim.fn.systemlist(cmd)
if #files == 0 then
vim.notify("No modified files since " .. base_branch, vim.log.levels.INFO)
return
end
pickers.new({}, {
prompt_title = "Modified Files (vs " .. base_branch .. ")",
finder = finders.new_table({
results = files,
entry_maker = function(entry)
return {
value = entry,
display = entry,
ordinal = entry,
path = entry,
}
end,
}),
sorter = conf.file_sorter({}),
previewer = previewers.new_termopen_previewer({
get_command = function(entry)
return { "git", "diff", base_branch .. "...HEAD", "--", entry.value }
end,
}),
attach_mappings = function(prompt_bufnr, map)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
vim.cmd("edit " .. selection.value)
end)
return true
end,
initial_mode = "normal",
layout_strategy = "horizontal",
layout_config = {
horizontal = {
preview_width = 0.6,
width = 0.95,
height = 0.95,
},
},
}):find()
end, desc = "Modified files vs main" },
-- Commit search
{ "<leader>g/", function()
require("telescope.builtin").git_commits({
initial_mode = "normal",
git_command = { "git", "log", "--pretty=format:%h %ad %an %s", "--date=short", "--abbrev-commit", "--all", "--", "." },
})
end, desc = "Search git commits" },
-- Branch comparison
{ "<leader>gc", function()
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local previewers = require("telescope.previewers")
local current_branch = vim.fn.system("git branch --show-current"):gsub("\n", "")
local branches = vim.fn.systemlist("git branch -a --format='%(refname:short)'")
pickers.new({}, {
prompt_title = "Compare branch with " .. current_branch,
finder = finders.new_table({
results = branches,
entry_maker = function(entry)
return { value = entry, display = entry, ordinal = entry }
end,
}),
sorter = conf.generic_sorter({}),
previewer = previewers.new_termopen_previewer({
get_command = function(entry)
return { "git", "log", "--oneline", "--graph", current_branch .. ".." .. entry.value }
end,
}),
attach_mappings = function(prompt_bufnr, map)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
actions.close(prompt_bufnr)
vim.cmd("tabnew | terminal git diff " .. current_branch .. ".." .. selection.value)
end)
map("i", "<C-f>", function()
local selection = action_state.get_selected_entry()
actions.close(prompt_bufnr)
local files = vim.fn.systemlist("git diff --name-only " .. current_branch .. ".." .. selection.value)
pickers.new({}, {
prompt_title = "Files changed: " .. current_branch .. ".." .. selection.value,
finder = finders.new_table({ results = files }),
sorter = conf.file_sorter({}),
previewer = previewers.new_termopen_previewer({
get_command = function(entry)
return { "git", "diff", current_branch .. ".." .. selection.value, "--", entry[1] }
end,
}),
}):find()
end)
return true
end,
initial_mode = "insert",
}):find()
end, desc = "Compare branches" },
-- Unique commits between branches
{ "<leader>gu", function()
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local previewers = require("telescope.previewers")
local current_branch = vim.fn.system("git branch --show-current"):gsub("\n", "")
local branches = vim.fn.systemlist("git branch -a --format='%(refname:short)'")
pickers.new({}, {
prompt_title = "Compare unique commits with " .. current_branch,
finder = finders.new_table({ results = branches }),
sorter = conf.generic_sorter({}),
initial_mode = "normal",
attach_mappings = function(prompt_bufnr, map)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
actions.close(prompt_bufnr)
local target_branch = selection[1]
local outgoing = vim.fn.systemlist("git log " .. target_branch .. ".." .. current_branch .. " --oneline --no-merges")
local incoming = vim.fn.systemlist("git log " .. current_branch .. ".." .. target_branch .. " --oneline --no-merges")
local results = {}
for _, c in ipairs(outgoing) do
local hash = c:match("^(%S+)")
if hash then table.insert(results, { commit = c, direction = "↑ out", hash = hash }) end
end
for _, c in ipairs(incoming) do
local hash = c:match("^(%S+)")
if hash then table.insert(results, { commit = c, direction = "↓ in", hash = hash }) end
end
if #results == 0 then
vim.notify("Branches are identical", vim.log.levels.INFO)
return
end
pickers.new({}, {
prompt_title = current_branch .. " <-> " .. target_branch .. " (unique commits)",
finder = finders.new_table({
results = results,
entry_maker = function(entry)
return { value = entry.hash, display = entry.direction .. " " .. entry.commit, ordinal = entry.commit }
end,
}),
sorter = conf.generic_sorter({}),
previewer = previewers.new_termopen_previewer({
get_command = function(entry) return { "git", "show", entry.value } end,
}),
attach_mappings = function(prompt_bufnr2, map2)
actions.select_default:replace(function()
local sel = action_state.get_selected_entry()
actions.close(prompt_bufnr2)
vim.cmd("tabnew | terminal git show " .. sel.value)
end)
return true
end,
initial_mode = "normal",
}):find()
end)
return true
end,
}):find()
end, desc = "Unique commits between branches" },
-- Reflog
{ "<leader>gR", function()
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local previewers = require("telescope.previewers")
local reflog = vim.fn.systemlist("git reflog --pretty=format:'%h|%gd|%gs|%ar'")
if #reflog == 0 then
vim.notify("No reflog entries found", vim.log.levels.INFO)
return
end
pickers.new({}, {
prompt_title = "Git Reflog",
finder = finders.new_table({
results = reflog,
entry_maker = function(entry)
local parts = vim.split(entry, "|")
if #parts >= 4 then
return { value = parts[1], display = string.format("%s %s %s (%s)", parts[1], parts[2], parts[3], parts[4]), ordinal = entry }
end
return { value = entry, display = entry, ordinal = entry }
end,
}),
sorter = conf.generic_sorter({}),
previewer = previewers.new_termopen_previewer({
get_command = function(entry) return { "git", "show", "--stat", entry.value } end,
}),
attach_mappings = function(prompt_bufnr, map)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
actions.close(prompt_bufnr)
vim.cmd("!git checkout " .. selection.value)
end)
map("i", "<C-r>", function()
local selection = action_state.get_selected_entry()
vim.ui.select({ "soft", "mixed", "hard", "cancel" }, { prompt = "Reset to " .. selection.value .. " using?" }, function(choice)
if choice and choice ~= "cancel" then
actions.close(prompt_bufnr)
vim.cmd("!git reset --" .. choice .. " " .. selection.value)
end
end)
end)
return true
end,
initial_mode = "normal",
}):find()
end, desc = "Git reflog" },
-- Git blame history for line
{ "<leader>gB", function()
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local previewers = require("telescope.previewers")
local current_file = vim.fn.expand("%:p")
local relfile = vim.fn.expand("%")
if current_file == "" then
vim.notify("No file open", vim.log.levels.WARN)
return
end
local current_line = vim.fn.line(".")
local commits = vim.fn.systemlist(
"git log --pretty=format:'%h|%ad|%an|%s' --date=short -L " .. current_line .. "," .. current_line .. ":" .. vim.fn.shellescape(current_file)
)
local filtered = {}
for _, line in ipairs(commits) do
if line:match("^%w+|") then table.insert(filtered, line) end
end
if #filtered == 0 then
vim.notify("No commits found for this line", vim.log.levels.INFO)
return
end
pickers.new({}, {
prompt_title = "Line History: " .. relfile .. ":" .. current_line,
finder = finders.new_table({
results = filtered,
entry_maker = function(entry)
local parts = vim.split(entry, "|")
if #parts >= 4 then
local hash, date, author = parts[1], parts[2], parts[3]
local msg = table.concat({ unpack(parts, 4) }, "|")
return { value = hash, display = string.format("%s %s %s %s", hash, date, author, msg), ordinal = entry }
end
return { value = entry, display = entry, ordinal = entry }
end,
}),
sorter = conf.generic_sorter({}),
previewer = previewers.new_termopen_previewer({
get_command = function(entry) return { "git", "show", entry.value, "--", current_file } end,
}),
attach_mappings = function(prompt_bufnr, map)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
actions.close(prompt_bufnr)
vim.cmd("tabnew | terminal git show " .. selection.value)
end)
return true
end,
initial_mode = "normal",
}):find()
end, desc = "Git blame history for line" },
},
},
}
return {
"adibhanna/laravel.nvim",
dependencies = {
"MunifTanjim/nui.nvim",
"nvim-lua/plenary.nvim",
},
cond = function()
return vim.fn.filereadable(vim.fn.getcwd() .. "/artisan") == 1
end,
event = "VeryLazy",
opts = {
notifications = false,
debug = false,
keymaps = false, -- we'll set our own with <leader>l prefix
sail = {
enabled = true,
auto_detect = true,
},
},
config = function(_, opts)
require("laravel").setup(opts)
-- Register which-key group for Laravel (only in Laravel projects)
local ok, wk = pcall(require, "which-key")
if ok then
wk.add({
{ "<leader>L", group = "Laravel" },
{ "<leader>Ls", group = "Sail" },
})
end
-- Jump to Laravel config/route/view string under cursor
local function goto_laravel_config_string()
local line = vim.api.nvim_get_current_line()
local col = vim.api.nvim_win_get_cursor(0)[2] + 1
local root = vim.fn.getcwd()
-- Find the string under cursor: match function('string') patterns with position tracking
local str = nil
local func_name = nil
local search_start = 1
while true do
local fn_start, fn_end, fn, q = line:find("(%w+)%((['\"])", search_start)
if not fn_start then break end
local str_start = fn_end + 1
local str_end = line:find(q, str_start, true)
if str_end then
if col >= fn_start and col <= str_end then
str = line:sub(str_start, str_end - 1)
func_name = fn
break
end
search_start = str_end + 1
else
break
end
end
-- If no function pattern found, check context-specific navigation
if not str then
local filepath = vim.fn.expand("%:p")
local escaped_root = root:gsub("([%-%.%+%[%]%(%)%$%^%%%?%*])", "%%%1")
-- Config file reverse lookup: 'default' => ... in config/filesystems.php → grep config('filesystems.default')
local config_name = filepath:match(escaped_root .. "/config/(.+)%.php$")
if config_name then
local key = line:match("['\"]([%w_%-]+)['\"]%s*=>")
if key then
config_name = config_name:gsub("/", ".")
require("telescope.builtin").live_grep({
default_text = config_name .. "." .. key,
initial_mode = "normal",
})
return true
end
end
-- Route file: 'Controller@method' pattern
local controller_str, method = line:match("['\"]([%w\\]+)@(%w+)['\"]")
if controller_str then
local word = vim.fn.expand("<cword>")
-- Extract just the class name (last part after \)
local class_name = controller_str:match("([%w]+)$")
-- Build namespace path: Server\ManageController → Server/ManageController
local rel_path = controller_str:gsub("\\", "/")
if word == method or word == method:sub(1, #word) then
-- Cursor on method name → jump to method in controller
local candidates = vim.fn.globpath(root .. "/app", "**/" .. rel_path .. ".php", false, true)
if #candidates > 0 then
vim.cmd("edit " .. candidates[1])
vim.fn.search("function\\s\\+" .. method, "w")
return true
end
elseif word == class_name or controller_str:find(word, 1, true) then
-- Cursor on controller name → jump to controller
local candidates = vim.fn.globpath(root .. "/app", "**/" .. rel_path .. ".php", false, true)
if #candidates > 0 then
vim.cmd("edit " .. candidates[1])
return true
end
end
end
-- Controller file: cursor on a function definition → find route referencing it
local is_controller = filepath:match("Controller%.php$")
if is_controller then
local method_name = line:match("function%s+(%w+)")
if method_name then
local class_name = filepath:match("([%w]+)%.php$")
if class_name then
-- Search both old ('Controller@method') and new ([Controller::class, 'method']) syntax
require("telescope.builtin").live_grep({
default_text = class_name .. ".*" .. method_name,
search_dirs = {
root .. "/routes",
root .. "/app/Http",
},
initial_mode = "normal",
})
return true
end
end
end
return false
end
if func_name == "config" then
-- config('spark.trial_days') → config/spark.php, search for 'trial_days'
local parts = vim.split(str, ".", { plain = true })
local file = root .. "/config/" .. parts[1] .. ".php"
if vim.fn.filereadable(file) == 1 then
vim.cmd("edit " .. file)
if parts[2] then
vim.fn.search("['\"]" .. parts[#parts] .. "['\"]", "w")
end
return true
end
elseif func_name == "view" or func_name == "markdown" then
-- view('emails.welcome') → resources/views/emails/welcome.blade.php
local path = str:gsub("%.", "/")
local file = root .. "/resources/views/" .. path .. ".blade.php"
if vim.fn.filereadable(file) == 1 then
vim.cmd("edit " .. file)
return true
end
elseif func_name == "route" then
-- route('login') → search routes/ for 'login'
vim.cmd("Telescope live_grep cwd=" .. root .. "/routes default_text=" .. str)
return true
elseif func_name == "trans" or func_name == "__" or func_name == "lang" then
-- __('auth.failed') → lang/en/auth.php, search for 'failed'
local parts = vim.split(str, ".", { plain = true })
local file = root .. "/lang/en/" .. parts[1] .. ".php"
if vim.fn.filereadable(file) == 1 then
vim.cmd("edit " .. file)
if parts[2] then
vim.fn.search("['\"]" .. parts[#parts] .. "['\"]", "w")
end
return true
end
end
return false
end
-- Smart goto: Toggle between definition and references, with Laravel fallback
local function smart_goto()
-- Try Laravel config string navigation first (config, view, route, trans)
if goto_laravel_config_string() then return end
local client = vim.lsp.get_clients({ bufnr = 0 })[1]
local encoding = client and client.offset_encoding or "utf-16"
local params = vim.lsp.util.make_position_params(0, encoding)
local current_buf = vim.api.nvim_get_current_buf()
local current_pos = vim.api.nvim_win_get_cursor(0)
vim.lsp.buf_request(current_buf, 'textDocument/definition', params, function(err, result, ctx, config)
if err or not result or vim.tbl_isempty(result) then
vim.lsp.buf.references()
return
end
-- Normalize the result (can be Location or Location[])
local locations = result[1] and result or { result }
-- Check if we're at any of the definition locations
local at_definition = false
for _, location in ipairs(locations) do
local def_uri = location.uri or location.targetUri
local def_range = location.range or location.targetRange
if def_uri then
local def_bufnr = vim.uri_to_bufnr(def_uri)
if def_bufnr == current_buf and def_range then
local start_line = def_range.start.line + 1
local end_line = def_range['end'].line + 1
if current_pos[1] >= start_line and current_pos[1] <= end_line then
at_definition = true
break
end
end
end
end
if at_definition then
-- At definition, show references
require('telescope.builtin').lsp_references({ initial_mode = "normal" })
else
-- Not at definition, go to it
vim.lsp.buf.definition()
end
end)
end
-- Override gd and <leader>t after LSP attaches
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local ft = vim.bo[args.buf].filetype
if ft ~= "php" and ft ~= "blade" then return end
vim.keymap.set("n", "gd", smart_goto, { buffer = args.buf, desc = "Smart goto (LSP + Laravel)" })
vim.keymap.set("n", "<leader>t", smart_goto, { buffer = args.buf, desc = "Smart goto (LSP + Laravel)" })
end,
})
-- Laravel keymaps with <leader>L prefix (global, since this file only loads in Laravel projects)
vim.keymap.set("n", "<leader>Lc", function() require("laravel.navigate").goto_controller() end, { desc = "Go to controller" })
vim.keymap.set("n", "<leader>Lr", function() require("laravel.routes").show_routes() end, { desc = "Show routes" })
vim.keymap.set("n", "<leader>Ll", function()
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local conf = require("telescope.config").values
-- Get all commands starting with Laravel, Sail, or Artisan
local all_commands = vim.api.nvim_get_commands({})
local laravel_commands = {}
for name, _ in pairs(all_commands) do
if name:match("^Laravel") or name:match("^Sail") or name:match("^Artisan") then
table.insert(laravel_commands, name)
end
end
table.sort(laravel_commands)
pickers.new({}, {
prompt_title = "Laravel Commands",
initial_mode = "normal",
finder = finders.new_table({ results = laravel_commands }),
sorter = conf.generic_sorter({}),
attach_mappings = function(prompt_bufnr)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry()
vim.cmd(selection.value)
end)
return true
end,
}):find()
end, { desc = "Laravel commands" })
-- Sail
vim.keymap.set("n", "<leader>Lsu", "<cmd>SailUp<cr>", { desc = "Sail up" })
vim.keymap.set("n", "<leader>Lsd", "<cmd>SailDown<cr>", { desc = "Sail down" })
vim.keymap.set("n", "<leader>Lsr", "<cmd>SailRestart<cr>", { desc = "Sail restart" })
vim.keymap.set("n", "<leader>Lsh", "<cmd>SailShell<cr>", { desc = "Sail shell" })
end,
}
return {
"ThePrimeagen/harpoon",
branch = "harpoon2",
dependencies = { "nvim-lua/plenary.nvim" },
keys = {
{
"<leader>m",
function()
require("harpoon"):list():add()
end,
desc = "Harpoon add file",
},
{
"<C-e>",
function()
local harpoon = require("harpoon")
harpoon.ui:toggle_quick_menu(harpoon:list())
end,
desc = "Harpoon menu",
},
{
"<leader>j",
function()
require("harpoon"):list():select(1)
end,
desc = "Harpoon file 1",
},
{
"<leader>k",
function()
require("harpoon"):list():select(2)
end,
desc = "Harpoon file 2",
},
{
"<leader>l",
function()
require("harpoon"):list():select(3)
end,
desc = "Harpoon file 3",
},
},
config = function()
require("harpoon"):setup({
settings = {
save_on_toggle = true,
save_on_change = true,
},
})
end,
}
return {
"christoomey/vim-tmux-navigator",
cmd = {
"TmuxNavigateLeft",
"TmuxNavigateDown",
"TmuxNavigateUp",
"TmuxNavigateRight",
"TmuxNavigatePrevious",
"TmuxNavigatorProcessList",
},
keys = {
{ "<c-h>", "<cmd><C-U>TmuxNavigateLeft<cr>" },
{ "<c-j>", "<cmd><C-U>TmuxNavigateDown<cr>" },
{ "<c-k>", "<cmd><C-U>TmuxNavigateUp<cr>" },
{ "<c-l>", "<cmd><C-U>TmuxNavigateRight<cr>" },
{ "<c-\\>", "<cmd><C-U>TmuxNavigatePrevious<cr>" },
},
}
return {
{
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
{ "folke/neoconf.nvim", cmd = "Neoconf", config = false, dependencies = { "nvim-lspconfig" } },
"mason.nvim",
"mason-org/mason-lspconfig.nvim",
{
"hrsh7th/nvim-cmp",
dependencies = { "hrsh7th/cmp-nvim-lsp" }
},
{
"nvim-telescope/telescope.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
},
},
},
opts = {
diagnostics = {
underline = true,
update_in_insert = false,
virtual_text = {
spacing = 4,
source = "if_many",
prefix = "●",
},
severity_sort = true,
},
inlay_hints = {
enabled = false,
},
capabilities = {},
format = {
formatting_options = nil,
timeout_ms = 3000,
},
servers = {
-- Automatically configure common language servers
bashls = {},
cssls = {},
dockerls = {},
docker_compose_language_service = {},
emmet_ls = {},
gopls = {},
html = {},
jsonls = {},
lua_ls = {
settings = {
Lua = {
workspace = {
checkThirdParty = false,
},
codeLens = {
enable = true,
},
completion = {
callSnippet = "Replace",
},
diagnostics = {
globals = { "vim" },
},
},
},
},
pyright = {},
tailwindcss = {
-- Only start if tailwind config exists (prevents runaway processes)
root_dir = function(fname)
local root_pattern = require("lspconfig.util").root_pattern(
"tailwind.config.js",
"tailwind.config.cjs",
"tailwind.config.mjs",
"tailwind.config.ts"
)
return root_pattern(fname)
end,
-- Don't attach to random files without a project
single_file_support = false,
-- Limit filetypes to reduce unnecessary spawning
filetypes = {
"html",
"css",
"scss",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"svelte",
},
},
terraformls = {
root_dir = function(fname)
local root_pattern = require("lspconfig.util").root_pattern(
"*.tf",
".terraform",
"terraform.tfstate"
)
return root_pattern(fname)
end,
},
ts_ls = {},
marksman = {
init_options = {
renderers = {
codeBlock = {
languageAliases = {}
}
}
},
settings = {}
},
yamlls = {},
-- Load Intelephense config from lsp-servers/
intelephense = (function()
local config_path = vim.fn.stdpath("config") .. "/lua/lsp-servers/intelephense.lua"
if vim.fn.filereadable(config_path) == 1 then
return dofile(config_path)
else
return { cmd = { "intelephense", "--stdio" }, filetypes = { "php" } }
end
end)(),
},
setup = {},
},
config = function(_, opts)
local servers = opts.servers
local has_cmp, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
local capabilities = vim.tbl_deep_extend(
"force",
{},
vim.lsp.protocol.make_client_capabilities(),
has_cmp and cmp_nvim_lsp.default_capabilities() or {},
opts.capabilities or {}
)
local function setup(server)
local server_opts = vim.tbl_deep_extend("force", {
capabilities = vim.deepcopy(capabilities),
}, servers[server] or {})
if opts.setup[server] then
if opts.setup[server](server, server_opts) then
return
end
elseif opts.setup["*"] then
if opts.setup["*"](server, server_opts) then
return
end
end
require("lspconfig")[server].setup(server_opts)
end
local have_mason, mlsp = pcall(require, "mason-lspconfig")
local ensure_installed = {} ---@type string[]
for server, server_opts in pairs(servers) do
-- Skip special keys that aren't actual server names
if server == "*" then
goto continue
end
if server_opts then
server_opts = server_opts == true and {} or server_opts
-- Only setup manually if explicitly disabled for mason
if server_opts.mason == false then
setup(server)
else
ensure_installed[#ensure_installed + 1] = server
end
end
::continue::
end
if have_mason then
mlsp.setup({ ensure_installed = ensure_installed, handlers = { setup } })
end
-- Custom diagnostic filtering for Laravel
local function filter_diagnostics(diagnostic)
-- Filter out "Non static method 'where' should not be called statically" for Laravel facades
if diagnostic.source == "intelephense" and diagnostic.message:match("Non static method") and diagnostic.message:match("should not be called statically") then
-- Check if it's a common Laravel facade method
local facade_methods = { "where", "find", "create", "update", "delete", "get", "first", "with", "select", "join", "orderBy", "groupBy", "having", "take", "skip", "count", "sum", "avg", "min", "max", "paginate", "latest", "oldest" }
for _, method in ipairs(facade_methods) do
if diagnostic.message:match("'" .. method .. "'") then
return false -- Filter out this diagnostic
end
end
end
-- Filter out undefined function/method errors for common Laravel helpers
if diagnostic.source == "intelephense" and diagnostic.message:match("Undefined") then
local laravel_helpers = { "request", "config", "route", "auth", "session", "cache", "view", "redirect", "response", "abort", "app", "bcrypt", "collect", "env", "event", "factory", "info", "logger", "old", "resolve", "url", "asset", "trans", "__", "dispatch", "dispatch_now", "optional", "now", "today", "validator", "storage", "cookie", "dd", "dump", "tap", "filled", "blank", "rescue", "throw_if", "throw_unless", "report", "retry", "value", "with" }
for _, helper in ipairs(laravel_helpers) do
if diagnostic.message:match("'" .. helper .. "'") then
return false -- Filter out this diagnostic
end
end
end
return true -- Keep all other diagnostics
end
-- Apply diagnostic filter
local orig_handler = vim.lsp.handlers["textDocument/publishDiagnostics"]
vim.lsp.handlers["textDocument/publishDiagnostics"] = function(err, result, ctx, config)
if result and result.diagnostics then
result.diagnostics = vim.tbl_filter(filter_diagnostics, result.diagnostics)
end
orig_handler(err, result, ctx, config)
end
-- Add key mappings for common LSP functions
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('UserLspConfig', {}),
callback = function(ev)
local opts = { buffer = ev.buf }
local client = vim.lsp.get_client_by_id(ev.data.client_id)
-- Debug: Check if client supports code actions
-- Commented out to avoid repetitive warnings for copilot
-- if client and not client.server_capabilities.codeActionProvider then
-- vim.notify(
-- string.format("LSP server '%s' does not support code actions for %s files",
-- client.name,
-- vim.bo[ev.buf].filetype
-- ),
-- vim.log.levels.WARN
-- )
-- end
-- Setup telescope keymaps
local function telescope_builtin(method, telescopeOpts)
return function()
require('telescope.builtin')[method](telescopeOpts)
end
end
-- Use Telescope for goto/reference actions with lazy loading
vim.keymap.set('n', 'gD', telescope_builtin('lsp_declarations', { initial_mode = "normal" }), opts)
vim.keymap.set('n', 'gd', telescope_builtin('lsp_definitions', { initial_mode = "normal" }), opts)
vim.keymap.set('n', 'gi', telescope_builtin('lsp_implementations', { initial_mode = "normal" }), opts)
vim.keymap.set('n', 'gr', telescope_builtin('lsp_references', { initial_mode = "normal" }), opts)
end,
})
-- Add command to check LSP status
vim.api.nvim_create_user_command('LspInfo', function()
local clients = vim.lsp.get_active_clients()
if #clients == 0 then
vim.notify("No active LSP clients", vim.log.levels.INFO)
return
end
local info = {}
for _, client in ipairs(clients) do
local capabilities = {}
if client.server_capabilities.codeActionProvider then
table.insert(capabilities, "codeActions")
end
if client.server_capabilities.definitionProvider then
table.insert(capabilities, "definition")
end
if client.server_capabilities.referencesProvider then
table.insert(capabilities, "references")
end
table.insert(info, string.format(
"%s (id=%d): %s",
client.name,
client.id,
table.concat(capabilities, ", ")
))
end
vim.notify(table.concat(info, "\n"), vim.log.levels.INFO)
end, { desc = "Show LSP server information" })
end,
},
}
return {
{
"ThePrimeagen/refactoring.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-treesitter/nvim-treesitter",
},
keys = {
{
"<leader>pp",
function()
require("refactoring").select_refactor({
show_success_message = true,
})
end,
mode = { "n", "x" },
desc = "Refactoring menu",
},
-- Quick access to common refactorings
{
"<leader>pe",
function() require("refactoring").refactor("Extract Function") end,
mode = "x",
desc = "Extract function",
},
{
"<leader>pf",
function() require("refactoring").refactor("Extract Function To File") end,
mode = "x",
desc = "Extract function to file",
},
{
"<leader>pv",
function() require("refactoring").refactor("Extract Variable") end,
mode = "x",
desc = "Extract variable",
},
{
"<leader>pi",
function() require("refactoring").refactor("Inline Variable") end,
mode = { "n", "x" },
desc = "Inline variable",
},
{
"<leader>pb",
function() require("refactoring").refactor("Extract Block") end,
mode = "x",
desc = "Extract block",
},
{
"<leader>pbf",
function() require("refactoring").refactor("Extract Block To File") end,
mode = "x",
desc = "Extract block to file",
},
-- File operations and imports
{
"<leader>pr",
function() vim.lsp.buf.rename() end,
mode = "n",
desc = "Rename symbol",
},
{
"<leader>po",
function()
-- Organize imports
vim.lsp.buf.code_action({
filter = function(action)
return action.title:match("Organize") or action.title:match("Import")
end,
apply = true,
})
end,
mode = "n",
desc = "Organize imports",
},
{
"<leader>pa",
function() vim.lsp.buf.code_action() end,
mode = { "n", "x" },
desc = "Code actions",
},
{
"<leader>pF",
function() vim.lsp.buf.format() end,
mode = { "n", "x" },
desc = "Format code",
},
},
config = function()
require("refactoring").setup({
prompt_func_return_type = {
go = false,
java = false,
cpp = false,
c = false,
h = false,
hpp = false,
cxx = false,
},
prompt_func_param_type = {
go = false,
java = false,
cpp = false,
c = false,
h = false,
hpp = false,
cxx = false,
},
printf_statements = {},
print_var_statements = {},
show_success_message = true,
})
-- Load refactoring Telescope extension
require("telescope").load_extension("refactoring")
-- Telescope refactoring helper
vim.keymap.set(
{"n", "x"},
"<leader>pR",
function() require('telescope').extensions.refactoring.refactors() end,
{ desc = "Telescope refactoring" }
)
-- File operations
vim.keymap.set("n", "<leader>pm", function()
local current_file = vim.fn.expand("%:p")
if current_file == "" then
vim.notify("No file to move", vim.log.levels.WARN)
return
end
vim.ui.input({
prompt = "Move file to: ",
default = current_file,
completion = "file",
}, function(new_path)
if new_path and new_path ~= current_file then
-- Create directory if it doesn't exist
local new_dir = vim.fn.fnamemodify(new_path, ":h")
vim.fn.mkdir(new_dir, "p")
-- Move the file
local success = vim.loop.fs_rename(current_file, new_path)
if success then
-- Update buffer
vim.cmd("saveas " .. new_path)
vim.fn.delete(current_file)
vim.notify("File moved to: " .. new_path)
else
vim.notify("Failed to move file", vim.log.levels.ERROR)
end
end
end)
end, { desc = "Move/rename file" })
-- Auto-import missing symbols
vim.keymap.set("n", "<leader>pI", function()
vim.lsp.buf.code_action({
filter = function(action)
return action.title:match("Import") or action.title:match("Add import")
end,
apply = true,
})
end, { desc = "Auto import" })
end,
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment