diff --git a/lua/bullets/actions.lua b/lua/bullets/actions.lua new file mode 100644 index 0000000..244e232 --- /dev/null +++ b/lua/bullets/actions.lua @@ -0,0 +1,41 @@ +local M = {} + +local function feed(keys) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), "n", false) +end + +function M.insert_new_bullet() + if vim.fn.mode() == "n" then + vim.cmd.startinsert({ bang = true }) + else + feed("") + end + + return "" +end + +function M.renumber_list() end + +function M.renumber_selection() end + +function M.toggle_checkbox() end + +function M.recompute_checkboxes() end + +function M.demote() end + +function M.promote() end + +function M.demote_visual() end + +function M.promote_visual() end + +function M.select_checkbox() end + +function M.select_checkbox_inside() end + +function M.select_bullet() end + +function M.select_bullet_text() end + +return M diff --git a/lua/bullets/config.lua b/lua/bullets/config.lua new file mode 100644 index 0000000..3b31213 --- /dev/null +++ b/lua/bullets/config.lua @@ -0,0 +1,31 @@ +local M = {} + +M.defaults = { + enabled_file_types = { "markdown", "text", "gitcommit" }, + enable_in_empty_buffers = false, + set_mappings = true, + mapping_leader = "", + custom_mappings = {}, + delete_last_bullet_if_empty = 1, + line_spacing = 1, + pad_right = true, + max_alpha_characters = 2, + enable_roman_list = true, + list_item_styles = { "-", "*+", ".+", "#.", "+", "\\item" }, + outline_levels = { "ROM", "ABC", "num", "abc", "rom", "std-", "std*", "std+" }, + renumber_on_change = true, + nested_checkboxes = true, + enable_wrapped_lines = true, + checkbox_markers = " .oOX", + checkbox_partials_toggle = 1, + auto_indent_after_colon = true, +} + +M.options = vim.deepcopy(M.defaults) + +function M.setup(options) + M.options = vim.tbl_deep_extend("force", vim.deepcopy(M.defaults), options or {}) + return M.options +end + +return M diff --git a/lua/bullets/init.lua b/lua/bullets/init.lua new file mode 100644 index 0000000..1d465cd --- /dev/null +++ b/lua/bullets/init.lua @@ -0,0 +1,160 @@ +local config = require("bullets.config") + +local M = {} + +local augroup = vim.api.nvim_create_augroup("bullets.nvim", { clear = true }) + +local function actions() + return require("bullets.actions") +end + +local function command(name, fn, opts) + vim.api.nvim_create_user_command(name, fn, vim.tbl_extend("force", { force = true }, opts or {})) +end + +local function map(mode, lhs, rhs, opts) + vim.keymap.set(mode, lhs, rhs, vim.tbl_extend("force", { silent = true }, opts or {})) +end + +local function map_buffer(buf, mode, lhs, rhs, opts) + vim.keymap.set(mode, lhs, rhs, vim.tbl_extend("force", { buffer = buf, silent = true }, opts or {})) +end + +local function add_commands() + command("InsertNewBullet", function() + actions().insert_new_bullet() + end) + command("RenumberList", function() + actions().renumber_list() + end) + command("RenumberSelection", function() + actions().renumber_selection() + end, { range = true }) + command("ToggleCheckbox", function() + actions().toggle_checkbox() + end) + command("RecomputeCheckboxes", function() + actions().recompute_checkboxes() + end) + command("BulletDemote", function() + actions().demote() + end) + command("BulletPromote", function() + actions().promote() + end) + command("BulletDemoteVisual", function() + actions().demote_visual() + end, { range = true }) + command("BulletPromoteVisual", function() + actions().promote_visual() + end, { range = true }) + command("SelectCheckbox", function() + actions().select_checkbox() + end) + command("SelectCheckboxInside", function() + actions().select_checkbox_inside() + end) + command("SelectBullet", function() + actions().select_bullet() + end) + command("SelectBulletText", function() + actions().select_bullet_text() + end) +end + +local function add_plug_mappings() + map("i", "(bullets-newline)", function() + return actions().insert_new_bullet() + end, { expr = true }) + map("n", "(bullets-newline)", function() + actions().insert_new_bullet() + end) + map("n", "(bullets-renumber)", function() + actions().renumber_list() + end) + map("x", "(bullets-renumber)", function() + actions().renumber_selection() + end) + map("n", "(bullets-toggle-checkbox)", function() + actions().toggle_checkbox() + end) + map("n", "(bullets-recompute-checkboxes)", function() + actions().recompute_checkboxes() + end) + map({ "i", "n" }, "(bullets-demote)", function() + actions().demote() + end) + map("x", "(bullets-demote)", function() + actions().demote_visual() + end) + map({ "i", "n" }, "(bullets-promote)", function() + actions().promote() + end) + map("x", "(bullets-promote)", function() + actions().promote_visual() + end) +end + +local function add_default_mappings(buf) + local opts = config.options + local leader = opts.mapping_leader + + map_buffer(buf, "i", leader .. "", "(bullets-newline)", { remap = true }) + map_buffer(buf, "i", leader .. "", "") + map_buffer(buf, "n", leader .. "o", "(bullets-newline)", { remap = true }) + map_buffer(buf, "n", leader .. "gN", "(bullets-renumber)", { remap = true }) + map_buffer(buf, "x", leader .. "gN", "(bullets-renumber)", { remap = true }) + map_buffer(buf, "n", leader .. "x", "(bullets-toggle-checkbox)", { remap = true }) + map_buffer(buf, "i", leader .. "", "(bullets-demote)", { remap = true }) + map_buffer(buf, "n", leader .. ">>", "(bullets-demote)", { remap = true }) + map_buffer(buf, "x", leader .. ">", "(bullets-demote)", { remap = true }) + map_buffer(buf, "i", leader .. "", "(bullets-promote)", { remap = true }) + map_buffer(buf, "n", leader .. "<<", "(bullets-promote)", { remap = true }) + map_buffer(buf, "x", leader .. "<", "(bullets-promote)", { remap = true }) +end + +local function add_custom_mappings(buf) + for _, item in ipairs(config.options.custom_mappings) do + map_buffer(buf, item[1], item[2], item[3], { remap = true }) + end +end + +local function configure_buffer(buf) + if config.options.set_mappings then + add_default_mappings(buf) + end + add_custom_mappings(buf) +end + +local function add_autocmds() + vim.api.nvim_clear_autocmds({ group = augroup }) + + vim.api.nvim_create_autocmd("FileType", { + group = augroup, + pattern = config.options.enabled_file_types, + callback = function(event) + configure_buffer(event.buf) + end, + }) + + if config.options.enable_in_empty_buffers then + vim.api.nvim_create_autocmd({ "BufNew", "BufRead" }, { + group = augroup, + pattern = "*", + callback = function(event) + if vim.bo[event.buf].filetype == "" then + configure_buffer(event.buf) + end + end, + }) + end +end + +function M.setup(options) + config.setup(options) + add_commands() + add_plug_mappings() + add_autocmds() +end + +return M diff --git a/mise.toml b/mise.toml index b8dcb73..412f373 100644 --- a/mise.toml +++ b/mise.toml @@ -25,11 +25,11 @@ if [ ! -d "$PLENARY_PATH" ]; then git clone --depth=1 https://github.com/nvim-lua/plenary.nvim "$PLENARY_PATH" fi status=0 -for spec in test/*_spec.lua; do - nvim --headless \ - -c "set rtp+=.,${PLENARY_PATH} | runtime plugin/plenary.vim | runtime plugin/bullets.vim" \ - --noplugin \ - -c "lua require('plenary.busted').run('${spec}')" || status=1 -done + for spec in test/*_spec.lua; do + nvim --headless \ + -c "set rtp+=.,${PLENARY_PATH} | runtime plugin/plenary.vim | runtime plugin/bullets.lua" \ + --noplugin \ + -c "lua require('plenary.busted').run('${spec}')" || status=1 + done exit $status """ diff --git a/plugin/bullets.lua b/plugin/bullets.lua new file mode 100644 index 0000000..a1cf7b7 --- /dev/null +++ b/plugin/bullets.lua @@ -0,0 +1,7 @@ +if vim.g.loaded_bullets_nvim then + return +end + +vim.g.loaded_bullets_nvim = true + +require("bullets").setup() diff --git a/test/helpers.lua b/test/helpers.lua index ae1ee52..cf5d61d 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -29,25 +29,27 @@ function M.test_bullet_inserted(second_bullet, initial_lines, expected_lines) assert.are.same(expected_lines, M.get_lines()) end --- Resets all bullets.vim globals to their plugin defaults. +-- Resets bullets.nvim to the plugin defaults used by the specs. -- Call in before_each for any describe block that mutates config. function M.reset_config() - vim.g.bullets_enabled_file_types = { "markdown", "text", "gitcommit", "scratch" } - vim.g.bullets_enable_in_empty_buffers = 1 - vim.g.bullets_set_mappings = 1 - vim.g.bullets_mapping_leader = "" - vim.g.bullets_custom_mappings = {} - vim.g.bullets_max_alpha_characters = 2 - vim.g.bullets_auto_indent_after_colon = 1 - vim.g.bullets_line_spacing = 1 - vim.g.bullets_renumber_on_change = 1 - vim.g.bullets_nested_checkboxes = 1 - vim.g.bullets_checkbox_markers = " .oOX" - vim.g.bullets_checkbox_partials_toggle = 1 - vim.g.bullets_outline_levels = { "ROM", "ABC", "num", "abc", "rom", "std-", "std*", "std+" } - vim.g.bullets_enable_roman_list = 1 - vim.g.bullets_pad_right = 1 - vim.g.bullets_delete_last_bullet_if_empty = 1 + require("bullets").setup({ + enabled_file_types = { "markdown", "text", "gitcommit", "scratch" }, + enable_in_empty_buffers = true, + set_mappings = true, + mapping_leader = "", + custom_mappings = {}, + max_alpha_characters = 2, + auto_indent_after_colon = true, + line_spacing = 1, + renumber_on_change = true, + nested_checkboxes = true, + checkbox_markers = " .oOX", + checkbox_partials_toggle = 1, + outline_levels = { "ROM", "ABC", "num", "abc", "rom", "std-", "std*", "std+" }, + enable_roman_list = true, + pad_right = true, + delete_last_bullet_if_empty = 1, + }) end return M diff --git a/test/minimal_init.lua b/test/minimal_init.lua index ecdb160..630245d 100644 --- a/test/minimal_init.lua +++ b/test/minimal_init.lua @@ -11,6 +11,6 @@ vim.opt.rtp:prepend(plenary_path) vim.opt.rtp:prepend(repo_root) vim.cmd("filetype plugin on") -vim.cmd("runtime plugin/bullets.vim") +vim.cmd("runtime plugin/bullets.lua") vim.cmd("set formatoptions=") vim.cmd("set noexpandtab") diff --git a/test/poc_spec.lua b/test/poc_spec.lua index b57dec5..27f8d18 100644 --- a/test/poc_spec.lua +++ b/test/poc_spec.lua @@ -2,14 +2,12 @@ local function feedkeys(keys) vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), "tx", false) end -local it = pending - describe("Bullets.vim", function() it("loads the plugin", function() assert.equals(2, vim.fn.exists(":InsertNewBullet")) end) - it("inserts a new bullet on ", function() + pending("inserts a new bullet on ", function() vim.cmd("enew") vim.bo.filetype = "text" vim.api.nvim_buf_set_lines(0, 0, -1, false, { "- first item" })