diff --git a/NEWS.md b/NEWS.md index 65503bc8..754615b9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,9 @@ ## 11.x +- `mini.ai` is back as a default plugin! Removing it was a mistake. + It's a great plugin that enhances the native text objects. + - `:LazyExtras` now has multiple new sections: - **Enabled**: extras that are currently enabled @@ -42,7 +45,6 @@ Additionally, some core plugins have been moved to extras. - plugins moved to extras: - - `mini.ai` which I couldn't live without, but not everyone needs it - `mini.surround` - `mini.indentscope` scopes are now also highlighted with `indent-blankline` - `nvim-treesitter-context` diff --git a/lua/lazyvim/config/init.lua b/lua/lazyvim/config/init.lua index de200eda..d990f94d 100644 --- a/lua/lazyvim/config/init.lua +++ b/lua/lazyvim/config/init.lua @@ -132,7 +132,7 @@ local defaults = { } M.json = { - version = 3, + version = 4, data = { version = nil, ---@type string? news = {}, ---@type table diff --git a/lua/lazyvim/plugins/coding.lua b/lua/lazyvim/plugins/coding.lua index 19c7785b..4c023141 100644 --- a/lua/lazyvim/plugins/coding.lua +++ b/lua/lazyvim/plugins/coding.lua @@ -182,4 +182,37 @@ return { import = "lazyvim.plugins.extras.coding.mini-comment", enabled = vim.fn.has("nvim-0.10") == 0, }, + + -- Better text-objects + { + "echasnovski/mini.ai", + event = "VeryLazy", + opts = function() + LazyVim.on_load("which-key.nvim", function() + vim.schedule(LazyVim.mini.ai_whichkey) + end) + local ai = require("mini.ai") + return { + n_lines = 500, + custom_textobjects = { + o = ai.gen_spec.treesitter({ -- code block + a = { "@block.outer", "@conditional.outer", "@loop.outer" }, + i = { "@block.inner", "@conditional.inner", "@loop.inner" }, + }), + f = ai.gen_spec.treesitter({ a = "@function.outer", i = "@function.inner" }), -- function + c = ai.gen_spec.treesitter({ a = "@class.outer", i = "@class.inner" }), -- class + t = { "<([%p%w]-)%f[^<%w][^<>]->.-", "^<.->().*()$" }, -- tags + d = { "%f[%d]%d+" }, -- digits + e = { -- Word with case + { "%u[%l%d]+%f[^%l%d]", "%f[%S][%l%d]+%f[^%l%d]", "%f[%P][%l%d]+%f[^%l%d]", "^[%l%d]+%f[^%l%d]" }, + "^().*()$", + }, + i = LazyVim.mini.ai_indent, -- indent + g = LazyVim.mini.ai_buffer, -- buffer + u = ai.gen_spec.function_call(), -- u for "Usage" + U = ai.gen_spec.function_call({ name_pattern = "[%w_]" }), -- without dot in function name + }, + } + end, + }, } diff --git a/lua/lazyvim/plugins/extras/coding/mini-ai.lua b/lua/lazyvim/plugins/extras/coding/mini-ai.lua deleted file mode 100644 index 7c4fdf70..00000000 --- a/lua/lazyvim/plugins/extras/coding/mini-ai.lua +++ /dev/null @@ -1,97 +0,0 @@ --- Better text-objects -return { - "echasnovski/mini.ai", - desc = "Enhanced text objects", - recommended = true, - -- keys = { - -- { "a", mode = { "x", "o" } }, - -- { "i", mode = { "x", "o" } }, - -- }, - event = "VeryLazy", - opts = function() - local ai = require("mini.ai") - return { - n_lines = 500, - custom_textobjects = { - o = ai.gen_spec.treesitter({ - a = { "@block.outer", "@conditional.outer", "@loop.outer" }, - i = { "@block.inner", "@conditional.inner", "@loop.inner" }, - }, {}), - f = ai.gen_spec.treesitter({ a = "@function.outer", i = "@function.inner" }, {}), - c = ai.gen_spec.treesitter({ a = "@class.outer", i = "@class.inner" }, {}), - t = { "<([%p%w]-)%f[^<%w][^<>]->.-", "^<.->().*()$" }, - d = { "%f[%d]%d+" }, -- digits - e = { -- Word with case - { - "%u[%l%d]+%f[^%l%d]", - "%f[%S][%l%d]+%f[^%l%d]", - "%f[%P][%l%d]+%f[^%l%d]", - "^[%l%d]+%f[^%l%d]", - }, - "^().*()$", - }, - g = function() -- Whole buffer, similar to `gg` and 'G' motion - local from = { line = 1, col = 1 } - local to = { - line = vim.fn.line("$"), - col = math.max(vim.fn.getline("$"):len(), 1), - } - return { from = from, to = to } - end, - u = ai.gen_spec.function_call(), -- u for "Usage" - U = ai.gen_spec.function_call({ name_pattern = "[%w_]" }), -- without dot in function name - }, - } - end, - config = function(_, opts) - require("mini.ai").setup(opts) - -- register all text objects with which-key - LazyVim.on_load("which-key.nvim", function() - ---@type table - local i = { - [" "] = "Whitespace", - ['"'] = 'Balanced "', - ["'"] = "Balanced '", - ["`"] = "Balanced `", - ["("] = "Balanced (", - [")"] = "Balanced ) including white-space", - [">"] = "Balanced > including white-space", - [""] = "Balanced <", - ["]"] = "Balanced ] including white-space", - ["["] = "Balanced [", - ["}"] = "Balanced } including white-space", - ["{"] = "Balanced {", - ["?"] = "User Prompt", - _ = "Underscore", - a = "Argument", - b = "Balanced ), ], }", - c = "Class", - d = "Digit(s)", - e = "Word in CamelCase & snake_case", - f = "Function", - g = "Entire file", - o = "Block, conditional, loop", - q = "Quote `, \", '", - t = "Tag", - u = "Use/call function & method", - U = "Use/call without dot in name", - } - local a = vim.deepcopy(i) - for k, v in pairs(a) do - a[k] = v:gsub(" including.*", "") - end - - local ic = vim.deepcopy(i) - local ac = vim.deepcopy(a) - for key, name in pairs({ n = "Next", l = "Last" }) do - i[key] = vim.tbl_extend("force", { name = "Inside " .. name .. " textobject" }, ic) - a[key] = vim.tbl_extend("force", { name = "Around " .. name .. " textobject" }, ac) - end - require("which-key").register({ - mode = { "o", "x" }, - i = i, - a = a, - }) - end) - end, -} diff --git a/lua/lazyvim/plugins/xtras.lua b/lua/lazyvim/plugins/xtras.lua index 39a561a1..6f50ec5b 100644 --- a/lua/lazyvim/plugins/xtras.lua +++ b/lua/lazyvim/plugins/xtras.lua @@ -9,16 +9,8 @@ local prios = { ["lazyvim.plugins.extras.dap.core"] = 1, } -local used = {} ---@type table - ---@type string[] -Config.json.data.extras = vim.tbl_filter(function(extra) - if used[extra] then - return false - end - used[extra] = true - return true -end, Config.json.data.extras) +Config.json.data.extras = LazyVim.dedup(Config.json.data.extras) table.sort(Config.json.data.extras, function(a, b) local pa = prios[a] or 10 diff --git a/lua/lazyvim/util/json.lua b/lua/lazyvim/util/json.lua index 3df7aecd..4196847e 100644 --- a/lua/lazyvim/util/json.lua +++ b/lua/lazyvim/util/json.lua @@ -78,6 +78,13 @@ function M.migrate() return extra == "lazyvim.plugins.extras.editor.symbols-outline" and "lazyvim.plugins.extras.editor.outline" or extra end, json.data.extras or {}) + elseif json.data.version == 3 then + json.data.extras = vim.tbl_filter(function(extra) + return not ( + extra == "lazyvim.plugins.extras.coding.mini-ai" + or extra == "lazyvim.plugins.extras.ui.treesitter-rewrite" + ) + end, json.data.extras or {}) end M.save() diff --git a/lua/lazyvim/util/mini.lua b/lua/lazyvim/util/mini.lua new file mode 100644 index 00000000..986a1887 --- /dev/null +++ b/lua/lazyvim/util/mini.lua @@ -0,0 +1,112 @@ +---@class lazyvim.util.mini +local M = {} + +---@alias Mini.ai.loc {line:number, col:number} +---@alias Mini.ai.region {from:Mini.ai.loc, to:Mini.ai.loc} + +-- Mini.ai indent text object +-- For "a", it will include the non-whitespace line surrounding the indent block. +-- "a" is line-wise, "i" is character-wise. +function M.ai_indent(ai_type) + local spaces = (" "):rep(vim.o.tabstop) + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + local indents = {} ---@type {line: number, indent: number, text: string}[] + + for l, line in ipairs(lines) do + if not line:find("^%s*$") then + indents[#indents + 1] = { line = l, indent = #line:gsub("\t", spaces):match("^%s*"), text = line } + end + end + + local ret = {} ---@type (Mini.ai.region | {indent: number})[] + + for i = 1, #indents do + if i == 1 or indents[i - 1].indent < indents[i].indent then + local from, to = i, i + for j = i + 1, #indents do + if indents[j].indent < indents[i].indent then + break + end + to = j + end + from = ai_type == "a" and from > 1 and from - 1 or from + to = ai_type == "a" and to < #indents and to + 1 or to + ret[#ret + 1] = { + indent = indents[i].indent, + from = { line = indents[from].line, col = ai_type == "a" and 1 or indents[from].indent + 1 }, + to = { line = indents[to].line, col = #indents[to].text }, + } + end + end + + return ret +end + +-- taken from MiniExtra.gen_ai_spec.buffer +function M.ai_buffer(ai_type) + local start_line, end_line = 1, vim.fn.line("$") + if ai_type == "i" then + -- Skip first and last blank lines for `i` textobject + local first_nonblank, last_nonblank = vim.fn.nextnonblank(start_line), vim.fn.prevnonblank(end_line) + -- Do nothing for buffer with all blanks + if first_nonblank == 0 or last_nonblank == 0 then + return { from = { line = start_line, col = 1 } } + end + start_line, end_line = first_nonblank, last_nonblank + end + + local to_col = math.max(vim.fn.getline(end_line):len(), 1) + return { from = { line = start_line, col = 1 }, to = { line = end_line, col = to_col } } +end + +-- register all text objects with which-key +function M.ai_whichkey() + ---@type table + local i = { + [" "] = "Whitespace", + ['"'] = 'Balanced "', + ["'"] = "Balanced '", + ["`"] = "Balanced `", + ["("] = "Balanced (", + [")"] = "Balanced ) including white-space", + [">"] = "Balanced > including white-space", + [""] = "Balanced <", + ["]"] = "Balanced ] including white-space", + ["["] = "Balanced [", + ["}"] = "Balanced } including white-space", + ["{"] = "Balanced {", + ["?"] = "User Prompt", + _ = "Underscore", + a = "Argument", + b = "Balanced ), ], }", + c = "Class", + d = "Digit(s)", + e = "Word in CamelCase & snake_case", + f = "Function", + g = "Entire file", + i = "Indent", + o = "Block, conditional, loop", + q = "Quote `, \", '", + t = "Tag", + u = "Use/call function & method", + U = "Use/call without dot in name", + } + local a = vim.deepcopy(i) + for k, v in pairs(a) do + a[k] = v:gsub(" including.*", "") + end + + local ic = vim.deepcopy(i) + local ac = vim.deepcopy(a) + for key, name in pairs({ n = "Next", l = "Last" }) do + i[key] = vim.tbl_extend("force", { name = "Inside " .. name .. " textobject" }, ic) + a[key] = vim.tbl_extend("force", { name = "Around " .. name .. " textobject" }, ac) + end + require("which-key").register({ + mode = { "o", "x" }, + i = i, + a = a, + }) +end + +return M diff --git a/lua/lazyvim/util/plugin.lua b/lua/lazyvim/util/plugin.lua index 0789b09f..99e66ae5 100644 --- a/lua/lazyvim/util/plugin.lua +++ b/lua/lazyvim/util/plugin.lua @@ -12,6 +12,7 @@ M.deprecated_extras = { ["lazyvim.plugins.extras.ui.dashboard"] = "`dashboard.nvim` is now the default **LazyVim** starter.", ["lazyvim.plugins.extras.coding.native_snippets"] = "Native snippets are now the default for **Neovim >= 0.10**", ["lazyvim.plugins.extras.ui.treesitter-rewrite"] = "Disabled `treesitter-rewrite` extra for now. Not ready yet.", + ["lazyvim.plugins.extras.coding.mini-ai"] = "`mini.ai` is now a code LazyVim plugin (again)", } M.deprecated_modules = {