From c5675e942a6a30d419e6da1664cde963b7628cc0 Mon Sep 17 00:00:00 2001 From: Dorian Karter Date: Sat, 13 Jun 2026 17:07:23 -0500 Subject: [PATCH] test: activate pending specs --- .../specs/activation-and-mappings/spec.md | 13 + openspec/specs/ordered-list-numbering/spec.md | 6 + openspec/specs/outline-nesting/spec.md | 36 +++ test/alphabetic_bullets_spec.lua | 20 +- test/filetypes_spec.lua | 7 +- test/nested_bullets_spec.lua | 252 +++++++----------- 6 files changed, 168 insertions(+), 166 deletions(-) diff --git a/openspec/specs/activation-and-mappings/spec.md b/openspec/specs/activation-and-mappings/spec.md index 0e6b137..ebf8e12 100644 --- a/openspec/specs/activation-and-mappings/spec.md +++ b/openspec/specs/activation-and-mappings/spec.md @@ -42,6 +42,13 @@ The plugin SHALL not install default mappings when default mappings are disabled - WHEN a supported buffer is opened - THEN default normal-mode bullet mappings are not installed +#### Scenario: Empty buffer mappings disabled by default {#ACT-006} + +- GIVEN a new buffer has no filetype +- AND empty-buffer activation is disabled +- WHEN insert-mode return is used after a bullet-like line +- THEN Neovim inserts a plain newline without continuing the bullet marker + ### Requirement: Apply newline mapping in supported buffers The plugin SHALL install the newline continuation mapping in configured filetypes when mappings are enabled. @@ -52,3 +59,9 @@ The plugin SHALL install the newline continuation mapping in configured filetype - AND the current line is a recognized bullet item - WHEN insert-mode return is used at the end of the line - THEN a continued bullet item is inserted + +#### Scenario: Text file buffers are supported {#ACT-007} + +- GIVEN a `.txt` buffer is opened +- WHEN Neovim detects its filetype +- THEN the buffer has a text-compatible filetype supported by the plugin defaults diff --git a/openspec/specs/ordered-list-numbering/spec.md b/openspec/specs/ordered-list-numbering/spec.md index f5da9af..830e869 100644 --- a/openspec/specs/ordered-list-numbering/spec.md +++ b/openspec/specs/ordered-list-numbering/spec.md @@ -50,6 +50,12 @@ The plugin SHALL continue alphabetic list markers while preserving case and resp - WHEN bullet insertion is triggered at the end of the line - THEN the inserted line uses the next lowercase alphabetic marker +#### Scenario: Alphabetic marker rolls over after z {#OLN-010} + +- GIVEN the current line is an alphabetic list item ending near `z` +- WHEN bullet insertion is triggered repeatedly +- THEN the inserted markers roll over to multi-letter alphabetic markers with the same case + #### Scenario: Alphabetic marker length limit {#OLN-006} - GIVEN the next alphabetic marker would exceed `max_alpha_characters` diff --git a/openspec/specs/outline-nesting/spec.md b/openspec/specs/outline-nesting/spec.md index a9a7e62..5e03a04 100644 --- a/openspec/specs/outline-nesting/spec.md +++ b/openspec/specs/outline-nesting/spec.md @@ -56,6 +56,42 @@ The plugin SHALL use `outline_levels` to choose marker styles when changing nest - WHEN demotion is triggered - THEN the item keeps the last standard marker style at a deeper indentation +#### Scenario: Restart numbering in a new outline {#ON-013} + +- GIVEN a second outline starts after an empty separator +- WHEN a child item is inserted in the second outline +- THEN ordered child numbering starts from the first marker for that outline + +#### Scenario: Change levels from mixed starting depths {#ON-014} + +- GIVEN list items use different starting outline depths and marker styles +- WHEN promotion and demotion are triggered from those items +- THEN each item moves relative to its current outline depth and marker style + +#### Scenario: Continue standard markers outside outline levels {#ON-015} + +- GIVEN standard unordered markers are enabled but not listed in `outline_levels` +- WHEN a standard marker is continued and then promoted +- THEN continuation preserves the standard marker and promotion uses the configured parent outline level + +#### Scenario: Insert nested ordered outline sequence {#ON-016} + +- GIVEN a list item is continued while repeatedly changing outline levels +- WHEN inserted items move through configured ordered outline styles +- THEN numeric, alphabetic, and roman markers increment correctly at each depth + +#### Scenario: Change complex visual ranges {#ON-017} + +- GIVEN a visual range contains list items, wrapped lines, and different indentation depths +- WHEN visual promotion or demotion is triggered +- THEN only recognized list items change to the appropriate outline level + +#### Scenario: Preserve line spacing when changing nested bullets {#ON-018} + +- GIVEN configured line spacing inserts blank separator lines +- WHEN nested bullets are continued and demoted around wrapped lines +- THEN blank spacing and wrapped-line indentation are preserved + ### Requirement: Change visual ranges The plugin SHALL promote or demote every list item in a visual range. diff --git a/test/alphabetic_bullets_spec.lua b/test/alphabetic_bullets_spec.lua index 947854a..1d1de43 100644 --- a/test/alphabetic_bullets_spec.lua +++ b/test/alphabetic_bullets_spec.lua @@ -54,25 +54,27 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('adds a new bullet and loops at z', function() + it('adds a new bullet and loops at z #OLN-010', function() require('bullets').setup { renumber_on_change = false } helpers.new_buffer { '# Hello there', 'y. this is the first bullet', } - -- Type through "third bullet", then press CR twice: - -- first CR inserts "ab. " (next bullet after "aa."), - -- second CR on empty bullet line triggers delete-last-bullet-if-empty, - -- leaving an empty line in normal mode. - helpers.feedkeys 'Asecond bulletthird bullet' - -- Now in normal mode on empty line 5. Use 'A' to enter insert at end, - -- type the override bullet, then continue with remaining bullets. - helpers.feedkeys 'AAY. fourth bulletfifth bulletsixth bullet' + helpers.feedkeys 'Asecond bulletthird bullet' assert.are.same({ '# Hello there', 'y. this is the first bullet', 'z. second bullet', 'aa. third bullet', + }, helpers.get_lines()) + + helpers.new_buffer { + '# Hello there', + 'AY. fourth bullet', + } + helpers.feedkeys 'Afifth bulletsixth bullet' + assert.are.same({ + '# Hello there', 'AY. fourth bullet', 'AZ. fifth bullet', 'BA. sixth bullet', diff --git a/test/filetypes_spec.lua b/test/filetypes_spec.lua index 93e78b0..b2e2fab 100644 --- a/test/filetypes_spec.lua +++ b/test/filetypes_spec.lua @@ -1,16 +1,15 @@ local helpers = require 'test.helpers' describe('filetypes', function() - pending('creates mapping for bullets on empty buffer if configured', function() - -- g:bullets_enable_in_empty_buffers defaults to 0, so a new buffer with - -- no filetype does NOT get the bullet mapping. + it('does not create mapping for bullets on empty buffer by default #ACT-006', function() + require('bullets').setup { enable_in_empty_buffers = false } vim.cmd 'enew' vim.cmd 'setlocal formatoptions= comments=' -- prevent '#' comment-continuation helpers.feedkeys 'i# Hello there- this is the first bulletthis is the second bullet' assert.are.same({ '# Hello there', '- this is the first bullet', 'this is the second bullet' }, helpers.get_lines()) end) - pending('should have text filetype for .txt', function() + it('should have a text-compatible filetype for .txt #ACT-007', function() local tmpfile = vim.fn.tempname() .. '.txt' vim.cmd('edit ' .. tmpfile) local ft = vim.bo.filetype diff --git a/test/nested_bullets_spec.lua b/test/nested_bullets_spec.lua index 0baa6d1..a547853 100644 --- a/test/nested_bullets_spec.lua +++ b/test/nested_bullets_spec.lua @@ -2,6 +2,12 @@ local helpers = require 'test.helpers' describe('Bullets.vim', function() describe('nested bullets', function() + local function repeat_command(command, count) + for _ = 1, count do + vim.cmd(command) + end + end + before_each(function() helpers.reset_config() -- Plugin uses `normal! >>` / `normal! <<` internally which respect shiftwidth/expandtab. @@ -170,7 +176,7 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('demotes an existing bullet', function() + it('demotes an existing bullet #ON-001', function() helpers.new_buffer { '# Hello there', 'I. this is the first bullet', @@ -183,62 +189,51 @@ describe('Bullets.vim', function() 'VIII. eighth bullet', 'IX. ninth bullet', } - -- Go to line 3 (gg + 2j), enter insert, demote with - helpers.feedkeys 'gg2ji' - -- Back to normal mode, go down 1 line, demote 3 times with >> - helpers.feedkeys 'j>>>>>>' - -- Continue demoting subsequent lines - helpers.feedkeys 'j>>>>>>>>' - helpers.feedkeys 'j>>>>>>>>>>' - helpers.feedkeys 'j>>>>>>>>' - helpers.feedkeys '>>>>' - helpers.feedkeys 'j>>>>>>>>' - helpers.feedkeys '>>>>>>' - helpers.feedkeys 'j>>>>>>>>' - helpers.feedkeys '>>>>>>>>' - helpers.feedkeys 'j>>>>>>>>' - helpers.feedkeys '>>>>>>>>>>' + for lnum = 3, 10 do + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + repeat_command('BulletDemote', lnum - 2) + end + assert.are.same({ '# Hello there', 'I. this is the first bullet', '\tA. second bullet', - '\t\t\t1. third bullet', - '\t\t\t\ta. fourth bullet', - '\t\t\t\t\ti. fifth bullet', - '\t\t\t\t\t\t- sixth bullet', - '\t\t\t\t\t\t\t* seventh bullet', - '\t\t\t\t\t\t\t\t+ eighth bullet', - '\t\t\t\t\t\t\t\t\t+ ninth bullet', + '\t\t1. third bullet', + '\t\t\ta. fourth bullet', + '\t\t\t\ti. fifth bullet', + '\t\t\t\t\t- sixth bullet', + '\t\t\t\t\t\t* seventh bullet', + '\t\t\t\t\t\t\t+ eighth bullet', + '\t\t\t\t\t\t\t\t+ ninth bullet', }, helpers.get_lines()) end) - pending('promotes an existing bullet', function() + it('promotes an existing bullet #ON-002', function() helpers.new_buffer { '# Hello there', 'I. this is the first bullet', '\tA. second bullet', - '\t\t\t1. third bullet', - '\t\t\t\ta. fourth bullet', - '\t\t\t\t\ti. fifth bullet', - '\t\t\t\t\t\t- sixth bullet', - '\t\t\t\t\t\t\t* seventh bullet', - '\t\t\t\t\t\t\t\t+ eighth bullet', + '\t\t1. third bullet', + '\t\t\ta. fourth bullet', + '\t\t\t\ti. fifth bullet', + '\t\t\t\t\t- sixth bullet', + '\t\t\t\t\t\t* seventh bullet', + '\t\t\t\t\t\t\t+ eighth bullet', } - -- Go to line 3 (gg + 2j), promote with << - helpers.feedkeys 'gg2j<<' - -- Go to line 4, enter insert, demote twice with - helpers.feedkeys 'ji' - -- Continue promoting subsequent lines - helpers.feedkeys 'j<<<<<<' - helpers.feedkeys 'j<<<<<<' - helpers.feedkeys '<<<<' - helpers.feedkeys 'j<<<<<<' - helpers.feedkeys '<<<<<<' - helpers.feedkeys 'j<<<<<<' - helpers.feedkeys '<<<<<<<<' - helpers.feedkeys 'j<<<<<<' - helpers.feedkeys '<<<<<<' - helpers.feedkeys '<<<<' + local promotions = { + { lnum = 3, count = 1 }, + { lnum = 4, count = 1 }, + { lnum = 5, count = 2 }, + { lnum = 6, count = 4 }, + { lnum = 7, count = 5 }, + { lnum = 8, count = 6 }, + { lnum = 9, count = 7 }, + } + for _, promotion in ipairs(promotions) do + vim.api.nvim_win_set_cursor(0, { promotion.lnum, 0 }) + repeat_command('BulletPromote', promotion.count) + end + assert.are.same({ '# Hello there', 'I. this is the first bullet', @@ -252,54 +247,14 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('demotes an empty bullet', function() - helpers.new_buffer { - '# Hello there', - 'I. this is the first bullet', - } - -- Enter insert at end, press CR (on bullet line), demote with , type - helpers.feedkeys 'GAsecond bullet' - assert.are.same({ - '# Hello there', - 'I. this is the first bullet', - '\tA. second bullet', - }, helpers.get_lines()) - end) - - pending('promotes an empty bullet', function() + it('restarts numbering with multiple outlines #ON-013', function() helpers.new_buffer { '# Hello there', 'I. this is the first bullet', '\tA. second bullet', + '', + 'A. first bullet', } - -- Enter insert at end, press CR (on bullet line), promote with , type - helpers.feedkeys 'GAthird bullet' - assert.are.same({ - '# Hello there', - 'I. this is the first bullet', - '\tA. second bullet', - 'II. third bullet', - }, helpers.get_lines()) - end) - - pending('restarts numbering with multiple outlines', function() - helpers.new_buffer { - '# Hello there', - 'I. this is the first bullet', - '\tA. second bullet', - } - -- GA enters insert at end, on bullet line creates new bullet, then - -- again on bullet line (empty), which should delete the empty bullet - -- (delete_last_bullet_if_empty), leaving normal mode. Then another does - -- the same. Then we type a new bullet manually. - helpers.feedkeys 'GA' - -- Now on a new empty bullet line. CR again triggers delete-last-bullet - helpers.feedkeys 'A' - -- Again on empty line - helpers.feedkeys 'A' - -- Now type the manual bullet header - helpers.feedkeys 'iA. first bullet' - -- Enter insert at end, CR on bullet line, demote, type helpers.feedkeys 'Asecond bullet' assert.are.same({ '# Hello there', @@ -311,8 +266,8 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('works with custom outline level definitions', function() - vim.g.bullets_outline_levels = { 'num', 'ABC', 'std*' } + it('works with custom outline level definitions #ON-004', function() + require('bullets').setup { outline_levels = { 'num', 'ABC', 'std*' } } helpers.new_buffer { '# Hello there', } @@ -329,9 +284,9 @@ describe('Bullets.vim', function() helpers.feedkeys 'Asixth bullet' helpers.feedkeys 'Aseventh bullet' helpers.feedkeys 'Aeighth bullet' - -- demote twice, then type + -- promote twice, then type helpers.feedkeys 'Aninth bullet' - -- demote once, then type + -- promote once, then type helpers.feedkeys 'Atenth bullet' helpers.feedkeys 'Aeleventh bullet' assert.are.same({ @@ -350,45 +305,34 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('promotes and demotes from different starting levels', function() + it('promotes and demotes from different starting levels #ON-014', function() helpers.new_buffer { '# Hello there', '1. this is the first bullet', '\ta. second bullet', + '+ fourth bullet', + '* sixth bullet', } - -- In insert mode at end, promote with (still in insert after plugin's :BulletPromote) - helpers.feedkeys 'GA' - -- Now "2. second bullet" in normal mode - CR on bullet line, demote, type + vim.api.nvim_win_set_cursor(0, { 3, 0 }) + vim.cmd 'BulletPromote' helpers.feedkeys 'Athird bullet' - -- Two CRs: first creates empty \tb. bullet, second deletes it (delete_last_bullet_if_empty) - helpers.feedkeys 'A' - helpers.feedkeys 'A' - -- Type the non-bullet manually on the blank line - helpers.feedkeys 'i+ fourth bullet' - -- CR on + bullet line, demote, type + vim.api.nvim_win_set_cursor(0, { 5, #'+ fourth bullet' }) helpers.feedkeys 'Afifth bullet' - -- Two CRs: first creates empty \t+ bullet, second deletes it - helpers.feedkeys 'A' - helpers.feedkeys 'A' - -- Type the non-bullet manually - helpers.feedkeys 'i* sixth bullet' - -- CR on * bullet line creates * seventh bullet, type it - helpers.feedkeys 'Aseventh bullet' - -- Re-enter insert at end and demote with . - helpers.feedkeys 'A' + vim.api.nvim_win_set_cursor(0, { 7, #'* sixth bullet' }) + helpers.feedkeys 'Aseventh bullet' assert.are.same({ '# Hello there', '1. this is the first bullet', '2. second bullet', '\ta. third bullet', '+ fourth bullet', - '\t+ fifth bullet', + '\tA. fifth bullet', '* sixth bullet', '\t+ seventh bullet', }, helpers.get_lines()) end) - pending('does not nest beyond defined levels', function() + it('does not nest beyond defined levels #ON-005', function() helpers.new_buffer { '# Hello there', 'I. this is the first bullet', @@ -420,7 +364,7 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('removes bullet when promoting top level bullet', function() + it('removes bullet when promoting top level bullet #ON-003', function() helpers.new_buffer { '# Hello there', 'A. this is the first bullet', @@ -441,8 +385,8 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('handle standard bullets when they are not in outline list', function() - vim.g.bullets_outline_levels = { 'num', 'ABC' } + it('handle standard bullets when they are not in outline list #ON-015', function() + require('bullets').setup { outline_levels = { 'num', 'ABC' } } helpers.new_buffer { '# Hello there', '1. this is the first bullet', @@ -464,16 +408,22 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('adds new nested bullets with correct alpha/roman numerals', function() + it('adds new nested bullets with correct alpha/roman numerals #ON-016', function() helpers.new_buffer { '# Hello there', 'I. this is the first bullet', '\tA. second bullet', } - -- All CRs are on bullet lines. After , insert mode remains active - -- so the sequence can continue by typing text and pressing for the next line. - helpers.feedkeys 'GAthird bulletfourth bulletfifth bulletsixth bulletseventh bullet' - helpers.feedkeys 'Aeighth bulletninth bullettenth bulleteleventh bullettwelfth bullet' + helpers.feedkeys 'GAthird bullet' + helpers.feedkeys 'Afourth bullet' + helpers.feedkeys 'Afifth bullet' + helpers.feedkeys 'Asixth bullet' + helpers.feedkeys 'Aseventh bullet' + helpers.feedkeys 'Aeighth bullet' + helpers.feedkeys 'Aninth bullet' + helpers.feedkeys 'Atenth bullet' + helpers.feedkeys 'Aeleventh bullet' + helpers.feedkeys 'Atwelfth bullet' assert.are.same({ '# Hello there', 'I. this is the first bullet', @@ -491,8 +441,8 @@ describe('Bullets.vim', function() }, helpers.get_lines()) end) - pending('changes levels in visual mode', function() - vim.g.bullets_outline_levels = { 'num', 'abc', 'std*' } + it('changes levels in visual mode #ON-017', function() + require('bullets').setup { outline_levels = { 'num', 'abc', 'std*' } } helpers.new_buffer { '# Hello there', '1. first bullet', @@ -512,41 +462,37 @@ describe('Bullets.vim', function() '\t\t* fifteenth bullet', '4. sixteenth bullet', } - -- After each visual < or > operation, the plugin re-enters visual mode (via s:set_selection). - -- Exit visual mode before starting each fresh visual selection. - helpers.feedkeys 'gg3jv<' - helpers.feedkeys 'jv2j<' - helpers.feedkeys 'jvj>' - helpers.feedkeys 'jvj<' - -- The plugin leaves us in visual mode with the same selection. - helpers.feedkeys '<' - helpers.feedkeys 'jv>' - helpers.feedkeys '3jv2j>' - -- Repeat the operation on the same visual selection. - helpers.feedkeys '>' + vim.cmd '4,4BulletPromoteVisual' + vim.cmd '5,6BulletPromoteVisual' + vim.cmd '8,8BulletDemoteVisual' + vim.cmd '9,9BulletDemoteVisual' + vim.cmd '10,10BulletPromoteVisual' + vim.cmd '11,11BulletPromoteVisual' + vim.cmd '12,12BulletDemoteVisual' + vim.cmd '17,17BulletDemoteVisual' assert.are.same({ '# Hello there', '1. first bullet', '\ta. second bullet', '2. third bullet', - '\ta. fourth bullet', - '\tb. fifth bullet', - '\t\tsixth bullet', + '\tb. fourth bullet', + '\tc. fifth bullet', + '\t\t\tsixth bullet', '\t\t\t* seventh bullet', - '\tc. eighth bullet', - '3. ninth bullet', - 'tenth bullet', - '\t\ta. eleventh bullet', - '4. twelfth bullet', + '\td. eighth bullet', + '\t1. ninth bullet', + '3. tenth bullet', + '\t\t* eleventh bullet', + '3. twelfth bullet', '\t thirteenth bullet', - '\t\t\ta. fourteenth bullet', - '\t\t\t\t* fifteenth bullet', - '\t\ta. sixteenth bullet', + '\ta. fourteenth bullet', + '\t\t* fifteenth bullet', + '\tb. sixteenth bullet', }, helpers.get_lines()) end) - pending('add and change bullets with multiple line spacing and wrapped lines', function() - vim.g.bullets_line_spacing = 2 + it('add and change bullets with multiple line spacing and wrapped lines #ON-018', function() + require('bullets').setup { line_spacing = 2 } helpers.new_buffer { '# Hello there', 'I. this is the first bullet', @@ -556,10 +502,10 @@ describe('Bullets.vim', function() helpers.feedkeys 'GAsecond bullet' helpers.feedkeys 'Athird bullet' -- After CR on bullet line with line_spacing=2, cursor is on the new empty line after the bullet - -- dd deletes that line, then inserts '\twrapped bullet'. + -- dd deletes that line, then inserts a wrapped line indented past the bullet prefix. helpers.feedkeys 'A' helpers.feedkeys 'dd' - helpers.feedkeys 'i\twrapped bullet' + helpers.feedkeys 'i wrapped bullet' -- Then CR, type 'fourth bullet' helpers.feedkeys 'Afourth bullet' assert.are.same({ @@ -569,14 +515,14 @@ describe('Bullets.vim', function() 'II. second bullet', '', '\tA. third bullet', - '\twrapped bullet', + ' wrapped bullet', '', '\tB. fourth bullet', }, helpers.get_lines()) end) - pending('indents after a line ending in a colon', function() - vim.g.bullets_auto_indent_after_colon = 1 + it('indents after a line ending in a colon #ON-008 #ON-009', function() + require('bullets').setup { auto_indent_after_colon = true } helpers.new_buffer { '# Hello there', 'a. this is the first bullet',