From 229213c3bd539a45dd3855d5d8a8f333cfdf21fa Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Mon, 2 Mar 2020 10:07:47 -0800 Subject: [PATCH 01/13] add support for nested outlines --- plugin/bullets.vim | 159 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 136 insertions(+), 23 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index 5dce508..f06df76 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -1,5 +1,5 @@ " Vim plugin for automated bulleted lists -" Last Change: February 23, 2020 +" Last Change: March 2, 2020 " Maintainer: Dorian Karter " License: MIT " FileTypes: markdown, text, gitcommit @@ -55,6 +55,12 @@ while s:power >= 0 let s:abc_max += pow(26,s:power) let s:power -= 1 endwhile + +if !exists('g:bullets_outline_levels') + " Capitalization matters: all caps will make the symbol caps, lower = lower + let g:bullets_outline_levels = ['ROM', 'ABC', 'num', 'abc', 'rom'] +endif + " ------------------------------------------------------ }}} " Parse Bullet Type ------------------------------------------- {{{ @@ -197,7 +203,7 @@ fun! s:match_checkbox_bullet_item(input_text) endfun fun! s:match_bullet_list_item(input_text) - let l:std_bullet_regex = '\v(^\s*(-|\*+|\.+|#\.|\\item)(\s+))(.*)' + let l:std_bullet_regex = '\v(^(\s*)(-|\*+|\.+|#\.|\\item)(\s+))(.*)' let l:matches = matchlist(a:input_text, l:std_bullet_regex) if empty(l:matches) @@ -205,14 +211,16 @@ fun! s:match_bullet_list_item(input_text) endif let l:bullet_length = strlen(l:matches[1]) - let l:whole_bullet = l:matches[1] - let l:trailing_space = l:matches[3] - let l:text_after_bullet = l:matches[4] + let l:leading_space = l:matches[2] + let l:bullet = l:matches[3] + let l:trailing_space = l:matches[4] + let l:text_after_bullet = l:matches[5] return { \ 'bullet_type': 'std', \ 'bullet_length': l:bullet_length, - \ 'whole_bullet': l:whole_bullet, + \ 'leading_space': l:leading_space, + \ 'bullet': l:bullet, \ 'trailing_space': l:trailing_space, \ 'text_after_bullet': l:text_after_bullet \ } @@ -220,17 +228,20 @@ endfun " ------------------------------------------------------- }}} " Resolve Bullet Type ----------------------------------- {{{ -fun! s:closest_bullet_types(from_line_num) +fun! s:closest_bullet_types(from_line_num, max_indent) let l:lnum = a:from_line_num let l:ltxt = getline(l:lnum) + let l:curr_indent = indent(l:lnum) let l:bullet_kinds = s:parse_bullet(l:lnum, l:ltxt) " Support for wrapped text bullets " DEMO: https://raw.githubusercontent.com/dkarter/bullets.vim/master/img/wrapped-bullets.gif - while l:lnum > 1 && s:is_indented(l:ltxt) && l:bullet_kinds == [] + while l:lnum > 1 && (l:curr_indent != 0 || l:bullet_kinds != []) + \ && (a:max_indent < l:curr_indent || l:bullet_kinds == []) let l:lnum = l:lnum - 1 let l:ltxt = getline(l:lnum) let l:bullet_kinds = s:parse_bullet(l:lnum, l:ltxt) + let l:curr_indent = indent(l:lnum) endwhile return l:bullet_kinds @@ -259,8 +270,14 @@ endfun " Roman Numeral vs Alphabetic Bullets ---------------------------------- {{{ fun! s:resolve_rom_or_abc(bullet_types) let l:first_type = a:bullet_types[0] - let l:prev_search_starting_line = get(l:first_type, 'starting_at_line_num') - 1 - let l:prev_bullet_types = s:closest_bullet_types(l:prev_search_starting_line) + let l:prev_search_starting_line = l:first_type.starting_at_line_num - 1 + let l:bullet_indent = indent(l:first_type.starting_at_line_num) + let l:prev_bullet_types = s:closest_bullet_types(l:prev_search_starting_line, l:bullet_indent) + + while l:prev_bullet_types != [] && l:bullet_indent > indent(l:prev_search_starting_line) + let l:prev_search_starting_line -= 1 + let l:prev_bullet_types = s:closest_bullet_types(l:prev_search_starting_line, l:bullet_indent) + endwhile if len(l:prev_bullet_types) == 0 @@ -319,37 +336,36 @@ fun! s:next_bullet_str(bullet) let l:bullet_type = get(a:bullet, 'bullet_type') if l:bullet_type ==# 'rom' - return s:next_rom_bullet(a:bullet) + let l:next_bullet_marker = s:next_rom_bullet(a:bullet) elseif l:bullet_type ==# 'abc' - return s:next_abc_bullet(a:bullet) + let l:next_bullet_marker = s:next_abc_bullet(a:bullet) elseif l:bullet_type ==# 'num' - return s:next_num_bullet(a:bullet) + let l:next_bullet_marker = s:next_num_bullet(a:bullet) elseif l:bullet_type ==# 'chk' - return s:next_chk_bullet(a:bullet) + let l:next_bullet_marker = s:next_chk_bullet(a:bullet) else - return a:bullet.whole_bullet + let l:next_bullet_marker = a:bullet.bullet endif + let l:closure = has_key(a:bullet, 'closure') ? a:bullet.closure : '' + return a:bullet.leading_space . l:next_bullet_marker . l:closure . ' ' endfun fun! s:next_rom_bullet(bullet) let l:islower = a:bullet.bullet ==# tolower(a:bullet.bullet) - let l:next_num = s:arabic2roman(s:roman2arabic(a:bullet.bullet) + 1, l:islower) - return a:bullet.leading_space . l:next_num . a:bullet.closure . ' ' + return s:arabic2roman(s:roman2arabic(a:bullet.bullet) + 1, l:islower) endfun fun! s:next_abc_bullet(bullet) let l:islower = a:bullet.bullet ==# tolower(a:bullet.bullet) - let l:next_num = s:dec2abc(s:abc2dec(a:bullet.bullet) + 1, l:islower) - return a:bullet.leading_space . l:next_num . a:bullet.closure . ' ' + return s:dec2abc(s:abc2dec(a:bullet.bullet) + 1, l:islower) endfun fun! s:next_num_bullet(bullet) - let l:next_num = a:bullet.bullet + 1 - return a:bullet.leading_space . l:next_num . a:bullet.closure . ' ' + return a:bullet.bullet + 1 endfun fun! s:next_chk_bullet(bullet) - return a:bullet.leading_space . '- [ ] ' + return '- [ ]' endfun " }}} @@ -363,7 +379,8 @@ endfun fun! s:insert_new_bullet() let l:curr_line_num = line('.') let l:next_line_num = l:curr_line_num + g:bullets_line_spacing - let l:closest_bullet_types = s:closest_bullet_types(l:curr_line_num) + let l:curr_indent = indent(l:curr_line_num) + let l:closest_bullet_types = s:closest_bullet_types(l:curr_line_num, l:curr_indent) let l:bullet = s:resolve_bullet_type(l:closest_bullet_types) " need to find which line starts the previous bullet started at and start " searching up from there @@ -573,6 +590,95 @@ endfun command! -range=% RenumberSelection call renumber_selection() " --------------------------------------------------------- }}} +" Changing outline level ---------------------------------- {{{ +fun! s:change_bullet_level(direction) + execute "" + let l:lnum = line('.') + let l:curr_indent = indent(l:lnum) + let l:curr_bullet= s:closest_bullet_types(l:lnum, l:curr_indent) + let l:curr_bullet = s:resolve_bullet_type(l:curr_bullet) + + if l:curr_bullet != {} + " Only change the bullet level if it's currently a bullet. + let l:curr_line = l:curr_bullet.starting_at_line_num + let l:closest_bullet = s:closest_bullet_types(l:curr_line - 1, l:curr_indent) + let l:closest_bullet = s:resolve_bullet_type(l:closest_bullet) + + if l:closest_bullet != {} + let l:islower = l:closest_bullet.bullet ==# tolower(l:closest_bullet.bullet) + let l:closest_type = l:islower ? l:closest_bullet.bullet_type : + \ toupper(l:closest_bullet.bullet_type) + let l:closest_index = index(g:bullets_outline_levels, l:closest_type) + + if l:closest_index >= 0 + let l:closest_indent = indent(l:closest_bullet.starting_at_line_num) + + if (l:curr_indent == l:closest_indent) + let l:next_bullet = s:next_bullet_str(l:closest_bullet) + let l:next_bullet_str = s:pad_to_length(l:next_bullet, l:closest_bullet.bullet_length) + \ . l:curr_bullet.text_after_bullet + + elseif l:closest_index + 1 < len(g:bullets_outline_levels) || l:curr_indent < l:closest_indent + let l:next_index = l:closest_index + 1 + let l:next_type = g:bullets_outline_levels[l:next_index] + let l:next_islower = l:next_type ==# tolower(l:next_type) + let l:trailing_space = ' ' + + if l:next_type ==? 'rom' + let l:next_num = s:arabic2roman(1, l:next_islower) + elseif l:next_type ==? 'abc' + let l:next_num = s:dec2abc(1, l:next_islower) + else + let l:next_num = '1' + endif + + let l:next_bullet_str = + \ l:curr_bullet.leading_space + \ . l:next_num + \ . l:curr_bullet.closure + \ . l:trailing_space + \ . l:curr_bullet.text_after_bullet + + else + " We're outside of the defined outline levels + let l:next_bullet_str = + \ l:curr_bullet.leading_space + \ . l:curr_bullet.text_after_bullet + endif + + if l:next_bullet_str !=# '' + call setline(l:lnum, l:next_bullet_str) + execute "normal! $" + + else + if g:bullets_delete_last_bullet_if_empty + let l:orig_line = s:parse_bullet(l:lnum, getline(l:lnum)) + if l:orig_line != [] + call setline(l:lnum, l:orig_line[0].leading_space . l:orig_line[0].text_after_bullet) + endif + endif + endif + endif + endif + + endif +endfun + +fun! s:bullet_demote() + execute "normal! i\" + call s:change_bullet_level(-1) +endfun +fun! s:bullet_promote() + execute "normal! i\" + call s:change_bullet_level(1) +endfun + +command! BulletDemote call bullet_demote() +command! BulletPromote call bullet_promote() + + +" --------------------------------------------------------- }}} + " Keyboard mappings --------------------------------------- {{{ fun! s:add_local_mapping(mapping_type, mapping, action) let l:file_types = join(g:bullets_enabled_file_types, ',') @@ -613,6 +719,13 @@ augroup TextBulletsMappings " Toggle checkbox call s:add_local_mapping('nnoremap', 'x', ':ToggleCheckbox') + + " Promote and Demote outline level + call s:add_local_mapping('inoremap', '', ':call bullet_demote()') + call s:add_local_mapping('nnoremap', '>>', ':call bullet_demote()') + call s:add_local_mapping('inoremap', '', ':call bullet_promote()') + call s:add_local_mapping('nnoremap', '<<', ':call bullet_promote()') + end augroup END " --------------------------------------------------------- }}} From bc6fa539d07807e186912b9df356891e3194defe Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Mon, 2 Mar 2020 10:08:01 -0800 Subject: [PATCH 02/13] update readme --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b008d58..b6fcf3d 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,12 @@ Capybara integration testing. ❤️ - [x] detect lists that have multiline bullets (should have no empty lines between lines). - [x] add alphabetic list -- [ ] support for intelligent alphanumeric indented bullets e.g. 1. \t a. \t 1. +- [x] support for intelligent alphanumeric indented bullets e.g. 1. \t a. \t 1. +- [ ] update documentation for nested bullets +- [ ] support for nested numerical bullets, e.g., 1. -> 1.1 -> 1.1.1, 1.1.2 +- [ ] change nested outline levels in visual mode +- [ ] support renumbering of alphabetical, roman numerals, and nested lists +- [ ] add option to turn non-bullet lines into new bullets with `C-t`/`>>` --- From 8abad840b16e27459f50c6bc70ee7edd7620209a Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Mon, 2 Mar 2020 10:08:16 -0800 Subject: [PATCH 03/13] add spec for nested bullets --- spec/alphabetic_bullets_spec.rb | 24 ++++ spec/nested_bullets_spec.rb | 247 ++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+) create mode 100644 spec/nested_bullets_spec.rb diff --git a/spec/alphabetic_bullets_spec.rb b/spec/alphabetic_bullets_spec.rb index 303dfd0..622f129 100644 --- a/spec/alphabetic_bullets_spec.rb +++ b/spec/alphabetic_bullets_spec.rb @@ -132,6 +132,30 @@ TEXT end + it 'does not add a new bullet when mixed case' do + test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) + # Hello there + Ab. this is the first bullet + INIT + # Hello there + Ab. this is the first bullet + not a bullet + EXPECTED + end + + it 'does not add a new alpha bullet with wrapped lines' do + test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) + # Hello there + a. first bullet might not catch + me. second line. + INIT + # Hello there + a. first bullet might not catch + me. second line. + not a bullet + EXPECTED + end + describe 'g:bullets_max_alpha_characters' do it 'stops adding items after configured max (default 2)' do filename = "#{SecureRandom.hex(6)}.txt" diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb new file mode 100644 index 0000000..acc84ff --- /dev/null +++ b/spec/nested_bullets_spec.rb @@ -0,0 +1,247 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Bullets.vim' do + describe 'nested bullets' do + it 'demotes an existing bullet' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + II. second bullet + III. third bullet + IV. fourth bullet + V. fifth bullet + TEXT + + vim.edit filename + vim.normal '2ji' + vim.feedkeys '\' + vim.normal 'j' + vim.feedkeys '>>>>>>' + vim.normal 'j' + vim.feedkeys '>>>>>>>>' + vim.normal 'j' + vim.feedkeys '>>>>>>>>>>' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + 1. third bullet + a. fourth bullet + i. fifth bullet + + TEXT + end + + it 'promotes an existing bullet' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + 1. third bullet + a. fourth bullet + i. fifth bullet + TEXT + + vim.edit filename + vim.normal '2j' + vim.feedkeys '<<' + vim.normal 'ji' + vim.feedkeys '\' + vim.normal 'j' + vim.feedkeys '<<<<' + vim.normal 'j' + vim.feedkeys '<<<\' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + II. second bullet + A. third bullet + B. fourth bullet + III. fifth bullet + + TEXT + end + + it 'demotes an empty bullet' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + TEXT + + vim.edit filename + vim.normal 'GA' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\\' + vim.type 'third bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + \tA. second bullet + \t\t\t1. third bullet + + TEXT + end + + it 'promotes an empty bullet' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + 1. third bullet + TEXT + + vim.edit filename + vim.normal 'GA' + vim.feedkeys '\' + vim.type 'fourth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'fifth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'sixth bullet' + vim.feedkeys '\' + vim.type 'seventh bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + 1. third bullet + 2. fourth bullet + B. fifth bullet + II. sixth bullet + III. seventh bullet + + TEXT + end + + it 'does not nest below specified levels' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + 1. third bullet + a. fourth bullet + i. fifth bullet + ii. sixth bullet + TEXT + + vim.edit filename + vim.normal '6j' + vim.feedkeys 'o' + vim.feedkeys '\' + vim.type 'not a bullet' + vim.feedkeys '\' + vim.type 'seventh bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + 1. third bullet + a. fourth bullet + i. fifth bullet + ii. sixth bullet + not a bullet + iii. seventh bullet + + TEXT + end + it 'adds new nested bullets with correct alpha/roman numerals' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + B. third bullet + C. fourth bullet + D. fifth bullet + E. sixth bullet + F. seventh bullet + G. eighth bullet + H. ninth bullet + I. tenth bullet + TEXT + + vim.edit filename + vim.normal 'GA' + vim.feedkeys '\' + vim.type 'eleventh bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'twelfth bullet' + vim.feedkeys '\' + vim.type 'thirteenth bullet' + vim.feedkeys '\' + vim.type 'fourteenth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'fifteenth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'sixteenth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'seventeenth bullet' + vim.feedkeys '\' + vim.type 'eighteenth bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + A. second bullet + B. third bullet + C. fourth bullet + D. fifth bullet + E. sixth bullet + F. seventh bullet + G. eighth bullet + H. ninth bullet + I. tenth bullet + J. eleventh bullet + II. twelfth bullet + III. thirteenth bullet + A. fourteenth bullet + 1. fifteenth bullet + a. sixteenth bullet + i. seventeenth bullet + ii. eighteenth bullet + + TEXT + end + end +end From 71e38c536356f47a634b4f1e66a399511011dfef Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Tue, 3 Mar 2020 13:51:10 -0800 Subject: [PATCH 04/13] nested bullets: promote/demote correctly when not at end of line --- plugin/bullets.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index f06df76..5fa0d52 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -665,11 +665,11 @@ fun! s:change_bullet_level(direction) endfun fun! s:bullet_demote() - execute "normal! i\" + execute "normal! a\" call s:change_bullet_level(-1) endfun fun! s:bullet_promote() - execute "normal! i\" + execute "normal! a\" call s:change_bullet_level(1) endfun From 3a32f654b7d46b31e0f776cc55338bfc3eab8b43 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Tue, 3 Mar 2020 14:30:41 -0800 Subject: [PATCH 05/13] nested bullets: remove bullet when promoting top level bullet --- plugin/bullets.vim | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index 5fa0d52..0bdcf26 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -592,8 +592,20 @@ command! -range=% RenumberSelection call renumber_selection() " Changing outline level ---------------------------------- {{{ fun! s:change_bullet_level(direction) - execute "" let l:lnum = line('.') + let l:curr_line = s:parse_bullet(l:lnum, getline(l:lnum)) + + if a:direction == 1 + if l:curr_line != [] && indent(l:lnum) == 0 + " Promoting a bullet at the highest level will delete the bullet + call setline(l:lnum, l:curr_line[0].text_after_bullet) + else + execute "normal! a\" + endif + else + execute "normal! a\" + endif + let l:curr_indent = indent(l:lnum) let l:curr_bullet= s:closest_bullet_types(l:lnum, l:curr_indent) let l:curr_bullet = s:resolve_bullet_type(l:curr_bullet) @@ -660,16 +672,13 @@ fun! s:change_bullet_level(direction) endif endif endif - endif endfun fun! s:bullet_demote() - execute "normal! a\" call s:change_bullet_level(-1) endfun fun! s:bullet_promote() - execute "normal! a\" call s:change_bullet_level(1) endfun From bcd8aa13b4da36c681950e3caf46941d87b6ee62 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Tue, 3 Mar 2020 14:31:14 -0800 Subject: [PATCH 06/13] additional tests for nested bullets --- spec/nested_bullets_spec.rb | 333 ++++++++++++++++++++++++++++++------ 1 file changed, 280 insertions(+), 53 deletions(-) diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb index acc84ff..c28057e 100644 --- a/spec/nested_bullets_spec.rb +++ b/spec/nested_bullets_spec.rb @@ -31,10 +31,10 @@ expect(file_contents).to eq normalize_string_indent(<<-TEXT) # Hello there I. this is the first bullet - A. second bullet - 1. third bullet - a. fourth bullet - i. fifth bullet + \tA. second bullet + \t\t\t1. third bullet + \t\t\t\ta. fourth bullet + \t\t\t\t\ti. fifth bullet TEXT end @@ -44,10 +44,10 @@ write_file(filename, <<-TEXT) # Hello there I. this is the first bullet - A. second bullet - 1. third bullet - a. fourth bullet - i. fifth bullet + \tA. second bullet + \t\t1. third bullet + \t\t\ta. fourth bullet + \t\t\t\ti. fifth bullet TEXT vim.edit filename @@ -68,8 +68,8 @@ # Hello there I. this is the first bullet II. second bullet - A. third bullet - B. fourth bullet + \tA. third bullet + \tB. fourth bullet III. fifth bullet TEXT @@ -103,13 +103,63 @@ TEXT end + it 'restarts numbering with multiple outlines' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + \tA. second bullet + \t\t1. third bullet + TEXT + + vim.edit filename + vim.normal 'GA' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'A. first bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type '1. first bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + \tA. second bullet + \t\t1. third bullet + + A. first bullet + \t1. second bullet + \t\ta. third bullet + + 1. first bullet + \ta. second bullet + \t\ti. third bullet + + TEXT + end + it 'promotes an empty bullet' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there I. this is the first bullet - A. second bullet - 1. third bullet + \tA. second bullet + \t\t1. third bullet TEXT vim.edit filename @@ -131,26 +181,140 @@ expect(file_contents).to eq normalize_string_indent(<<-TEXT) # Hello there I. this is the first bullet - A. second bullet - 1. third bullet - 2. fourth bullet - B. fifth bullet + \tA. second bullet + \t\t1. third bullet + \t\t2. fourth bullet + \tB. fifth bullet II. sixth bullet III. seventh bullet TEXT end - it 'does not nest below specified levels' do + it 'works with custom outline level definitions' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + TEXT + + vim.edit filename + vim.command "let g:bullets_outline_levels=['num','ABC','rom']" + vim.normal 'GA' + vim.feedkeys '\' + vim.type '1. first bullet' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' + vim.feedkeys '\' + vim.type 'fourth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'fifth bullet' + vim.feedkeys '\' + vim.type 'sixth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'not a bullet' + vim.feedkeys '\' + vim.type 'seventh bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'eighth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'ninth bullet' + vim.feedkeys '\' + vim.type 'tenth bullet' + + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + 1. first bullet + 2. second bullet + \tA. third bullet + \tB. fourth bullet + \t\ti. fifth bullet + \t\tii. sixth bullet + \t\t\tnot a bullet + \t\tiii. seventh bullet + \tC. eighth bullet + 3. ninth bullet + 4. tenth bullet + + TEXT + end + + it 'promotes and demotes from different starting levels' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + 1. this is the first bullet + 2. second bullet + TEXT + + vim.edit filename + vim.normal 'GA' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' + vim.normal '3hi' + vim.feedkeys '\' + vim.feedkeys '\' + vim.feedkeys '\' + vim.feedkeys '>>' + vim.type 'anot a bullet' + vim.feedkeys '\' + vim.type 'fourth bullet' + vim.feedkeys '\' + vim.type 'fifth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.feedkeys '<<' + vim.feedkeys 'i\' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'i. sixth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'not a bullet' + vim.feedkeys '\' + vim.type 'seventh bullet' + + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + 1. this is the first bullet + \ta. second bullet + \t\ti. third bullet + \t\t\tnot a bullet + \t\tii. fourth bullet + \tb. fifth bullet + + i. sixth bullet + \tnot a bullet + ii. seventh bullet + + TEXT + end + + it 'does not nest below defined levels' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there I. this is the first bullet - A. second bullet - 1. third bullet - a. fourth bullet - i. fifth bullet - ii. sixth bullet + \tA. second bullet + \t\t1. third bullet + \t\t\ta. fourth bullet + \t\t\t\ti. fifth bullet + \t\t\t\tii. sixth bullet TEXT vim.edit filename @@ -167,30 +331,93 @@ expect(file_contents).to eq normalize_string_indent(<<-TEXT) # Hello there I. this is the first bullet - A. second bullet - 1. third bullet - a. fourth bullet - i. fifth bullet - ii. sixth bullet - not a bullet - iii. seventh bullet + \tA. second bullet + \t\t1. third bullet + \t\t\ta. fourth bullet + \t\t\t\ti. fifth bullet + \t\t\t\tii. sixth bullet + \t\t\t\t\tnot a bullet + \t\t\t\tiii. seventh bullet + + TEXT + end + + it 'removes bullet when promoting top level bullet' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + A. this is the first bullet + + I. second bullet + \tA. third bullet + TEXT + + vim.edit filename + vim.normal 'j' + vim.feedkeys '<<' + vim.normal '3ji' + vim.feedkeys '\' + vim.feedkeys '\' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + this is the first bullet + + I. second bullet + third bullet + + TEXT + end + + it 'nested outlines handle standard bullets' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + 1. this is the first bullet + \t- standard bullet + TEXT + + vim.edit filename + vim.normal 'GA' + vim.feedkeys '\' + vim.type 'second standard bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.type 'third bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + 1. this is the first bullet + \t- standard bullet + \t- second standard bullet + 2. second bullet + 3. third bullet TEXT end + it 'adds new nested bullets with correct alpha/roman numerals' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there I. this is the first bullet - A. second bullet - B. third bullet - C. fourth bullet - D. fifth bullet - E. sixth bullet - F. seventh bullet - G. eighth bullet - H. ninth bullet - I. tenth bullet + \tA. second bullet + \tB. third bullet + \tC. fourth bullet + \tD. fifth bullet + \tE. sixth bullet + \tF. seventh bullet + \tG. eighth bullet + \tH. ninth bullet + \tI. tenth bullet TEXT vim.edit filename @@ -223,23 +450,23 @@ expect(file_contents).to eq normalize_string_indent(<<-TEXT) # Hello there I. this is the first bullet - A. second bullet - B. third bullet - C. fourth bullet - D. fifth bullet - E. sixth bullet - F. seventh bullet - G. eighth bullet - H. ninth bullet - I. tenth bullet - J. eleventh bullet + \tA. second bullet + \tB. third bullet + \tC. fourth bullet + \tD. fifth bullet + \tE. sixth bullet + \tF. seventh bullet + \tG. eighth bullet + \tH. ninth bullet + \tI. tenth bullet + \tJ. eleventh bullet II. twelfth bullet III. thirteenth bullet - A. fourteenth bullet - 1. fifteenth bullet - a. sixteenth bullet - i. seventeenth bullet - ii. eighteenth bullet + \tA. fourteenth bullet + \t\t1. fifteenth bullet + \t\t\ta. sixteenth bullet + \t\t\t\ti. seventeenth bullet + \t\t\t\tii. eighteenth bullet TEXT end From 8205a190145dd5802bd460e6496dd19c63b434c0 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Tue, 3 Mar 2020 14:31:43 -0800 Subject: [PATCH 07/13] disable wrapped-line alpha test --- spec/alphabetic_bullets_spec.rb | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/spec/alphabetic_bullets_spec.rb b/spec/alphabetic_bullets_spec.rb index 622f129..854f3ee 100644 --- a/spec/alphabetic_bullets_spec.rb +++ b/spec/alphabetic_bullets_spec.rb @@ -143,18 +143,21 @@ EXPECTED end - it 'does not add a new alpha bullet with wrapped lines' do - test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) - # Hello there - a. first bullet might not catch - me. second line. - INIT - # Hello there - a. first bullet might not catch - me. second line. - not a bullet - EXPECTED - end + # it 'does not add a new alpha bullet with wrapped lines' do + # # TODO: maybe take guidance from Pandoc and require two spaces after the + # closure to allow us to differentiate between bullets and abbreviations + # and words. Might also consider only allowing single letters. + # test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) + # # Hello there + # a. first bullet might not catch + # me. second line. + # INIT + # # Hello there + # a. first bullet might not catch + # me. second line. + # not a bullet + # EXPECTED + # end describe 'g:bullets_max_alpha_characters' do it 'stops adding items after configured max (default 2)' do From 41aff53bdce9bd5f2f07e48f4e9411557bf9b631 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Wed, 4 Mar 2020 19:19:39 -0800 Subject: [PATCH 08/13] add support for nested standard bullets --- plugin/bullets.vim | 17 ++- spec/nested_bullets_spec.rb | 294 ++++++++++++++++++++++++------------ 2 files changed, 214 insertions(+), 97 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index 0bdcf26..607dc22 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -58,7 +58,8 @@ endwhile if !exists('g:bullets_outline_levels') " Capitalization matters: all caps will make the symbol caps, lower = lower - let g:bullets_outline_levels = ['ROM', 'ABC', 'num', 'abc', 'rom'] + " Standard bullets should include the marker symbol after 'std' + let g:bullets_outline_levels = ['ROM', 'ABC', 'num', 'abc', 'rom', 'std-', 'std*', 'std+'] endif " ------------------------------------------------------ }}} @@ -203,7 +204,7 @@ fun! s:match_checkbox_bullet_item(input_text) endfun fun! s:match_bullet_list_item(input_text) - let l:std_bullet_regex = '\v(^(\s*)(-|\*+|\.+|#\.|\\item)(\s+))(.*)' + let l:std_bullet_regex = '\v(^(\s*)(-|\*+|\.+|#\.|\+|\\item)(\s+))(.*)' let l:matches = matchlist(a:input_text, l:std_bullet_regex) if empty(l:matches) @@ -221,6 +222,7 @@ fun! s:match_bullet_list_item(input_text) \ 'bullet_length': l:bullet_length, \ 'leading_space': l:leading_space, \ 'bullet': l:bullet, + \ 'closure': '', \ 'trailing_space': l:trailing_space, \ 'text_after_bullet': l:text_after_bullet \ } @@ -620,6 +622,9 @@ fun! s:change_bullet_level(direction) let l:islower = l:closest_bullet.bullet ==# tolower(l:closest_bullet.bullet) let l:closest_type = l:islower ? l:closest_bullet.bullet_type : \ toupper(l:closest_bullet.bullet_type) + if l:closest_bullet.bullet_type ==# 'std' + let l:closest_type = l:closest_type . l:closest_bullet.bullet + endif let l:closest_index = index(g:bullets_outline_levels, l:closest_type) if l:closest_index >= 0 @@ -636,12 +641,18 @@ fun! s:change_bullet_level(direction) let l:next_islower = l:next_type ==# tolower(l:next_type) let l:trailing_space = ' ' + let l:curr_bullet.closure = l:closest_bullet.closure + if l:next_type ==? 'rom' let l:next_num = s:arabic2roman(1, l:next_islower) elseif l:next_type ==? 'abc' let l:next_num = s:dec2abc(1, l:next_islower) - else + elseif l:next_type ==# 'num' let l:next_num = '1' + else + " standard bullet; l:next_type contains the bullet symbol to use + let l:next_num = strpart(l:next_type, len(l:next_type) - 1) + let l:curr_bullet.closure = '' endif let l:next_bullet_str = diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb index c28057e..4d28b72 100644 --- a/spec/nested_bullets_spec.rb +++ b/spec/nested_bullets_spec.rb @@ -13,6 +13,10 @@ III. third bullet IV. fourth bullet V. fifth bullet + VI. sixth bullet + VII. seventh bullet + VIII. eighth bullet + IX. ninth bullet TEXT vim.edit filename @@ -24,6 +28,14 @@ vim.feedkeys '>>>>>>>>' vim.normal 'j' vim.feedkeys '>>>>>>>>>>' + vim.normal 'j' + vim.feedkeys '>>>>>>>>>>>>' + vim.normal 'j' + vim.feedkeys '>>>>>>>>>>>>>>' + vim.normal 'j' + vim.feedkeys '>>>>>>>>>>>>>>>>' + vim.normal 'j' + vim.feedkeys '>>>>>>>>>>>>>>>>>>' vim.write file_contents = IO.read(filename) @@ -35,6 +47,10 @@ \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\tninth bullet TEXT end @@ -45,9 +61,12 @@ # Hello there I. this is the first bullet \tA. second bullet - \t\t1. third bullet - \t\t\ta. fourth bullet - \t\t\t\ti. fifth 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 TEXT vim.edit filename @@ -55,11 +74,17 @@ vim.feedkeys '<<' vim.normal 'ji' vim.feedkeys '\' + vim.feedkeys '\' + vim.normal 'j' + vim.feedkeys '<<<<<<' + vim.normal 'j' + vim.feedkeys '<<<<<<<<<<' vim.normal 'j' - vim.feedkeys '<<<<' + vim.feedkeys '<<<<<<<<<<<<' vim.normal 'j' - vim.feedkeys '<<<\' + vim.feedkeys '<<<<<<<<<<<<<<' + vim.normal 'j' + vim.feedkeys '<<<<<<<<<<<<<<<<' vim.write file_contents = IO.read(filename) @@ -71,6 +96,9 @@ \tA. third bullet \tB. fourth bullet III. fifth bullet + IV. sixth bullet + V. seventh bullet + VI. eighth bullet TEXT end @@ -90,6 +118,22 @@ vim.feedkeys '\' vim.feedkeys '\\' vim.type 'third bullet' + vim.feedkeys '\' + vim.feedkeys 'o' + vim.feedkeys '\' + vim.type 'fourth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'fifth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'sixth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'seventh bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'eighth bullet' vim.write file_contents = IO.read(filename) @@ -99,61 +143,66 @@ 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 TEXT end - it 'restarts numbering with multiple outlines' do + it 'promotes an empty bullet' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there I. this is the first bullet \tA. second bullet \t\t1. third bullet + \t\t\ta. fourth bullet + \t\t\t\ti. fifth bullet + \t\t\t\t\t- sixth bullet TEXT vim.edit filename vim.normal 'GA' vim.feedkeys '\' + vim.type 'seventh bullet' vim.feedkeys '\' - vim.type 'A. first bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'second bullet' - vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'third bullet' - vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'eighth bullet' vim.feedkeys '\' - vim.type '1. first bullet' + vim.feedkeys '\' + vim.type 'ninth bullet' vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'second bullet' + vim.feedkeys '\' + vim.type 'tenth bullet' vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'third bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'eleventh bullet' vim.write file_contents = IO.read(filename) expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - - A. first bullet - \t1. second bullet - \t\ta. third bullet - - 1. first bullet - \ta. second bullet - \t\ti. third bullet + # Hello there + I. this is the first bullet + \tA. second 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- seventh bullet + \t\t\t\tii. eighth bullet + \t\t\tb. ninth bullet + \t\t2. tenth bullet + II. eleventh bullet TEXT end - it 'promotes an empty bullet' do + it 'restarts numbering with multiple outlines' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there @@ -165,28 +214,53 @@ vim.edit filename vim.normal 'GA' vim.feedkeys '\' - vim.type 'fourth bullet' vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'fifth bullet' + vim.type 'A. first bullet' vim.feedkeys '\' - vim.feedkeys '\' - vim.type 'sixth bullet' + vim.feedkeys '\' + vim.type 'second bullet' vim.feedkeys '\' - vim.type 'seventh bullet' + vim.type 'third bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type '1. first bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type '- first bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' vim.write file_contents = IO.read(filename) expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - \t\t2. fourth bullet - \tB. fifth bullet - II. sixth bullet - III. seventh bullet + # Hello there + I. this is the first bullet + \tA. second bullet + \t\t1. third bullet + + A. first bullet + \t1. second bullet + \t\ta. third bullet + + 1. first bullet + \ta. second bullet + \t\ti. third bullet + + - first bullet + \t* second bullet + \t\t+ third bullet TEXT end @@ -198,7 +272,7 @@ TEXT vim.edit filename - vim.command "let g:bullets_outline_levels=['num','ABC','rom']" + vim.command "let g:bullets_outline_levels=['num','ABC','std*']" vim.normal 'GA' vim.feedkeys '\' vim.type '1. first bullet' @@ -238,10 +312,10 @@ 2. second bullet \tA. third bullet \tB. fourth bullet - \t\ti. fifth bullet - \t\tii. sixth bullet + \t\t* fifth bullet + \t\t* sixth bullet \t\t\tnot a bullet - \t\tiii. seventh bullet + \t\t* seventh bullet \tC. eighth bullet 3. ninth bullet 4. tenth bullet @@ -267,23 +341,30 @@ vim.feedkeys '\' vim.feedkeys '\' vim.feedkeys '>>' - vim.type 'anot a bullet' + vim.type 'astandard bullet' vim.feedkeys '\' vim.type 'fourth bullet' vim.feedkeys '\' vim.type 'fifth bullet' vim.feedkeys '\' + vim.feedkeys '\' vim.feedkeys '\' vim.feedkeys '<<' vim.feedkeys 'i\' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'i. sixth bullet' + vim.type '+ sixth bullet' vim.feedkeys '\' vim.feedkeys '\' vim.type 'not a bullet' vim.feedkeys '\' vim.type 'seventh bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type '* eighth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'ninth bullet' vim.write @@ -294,13 +375,16 @@ 1. this is the first bullet \ta. second bullet \t\ti. third bullet - \t\t\tnot a bullet - \t\tii. fourth bullet + \t\t\t- standard bullet + \t\t\t- fourth bullet \tb. fifth bullet - i. sixth bullet + + sixth bullet \tnot a bullet - ii. seventh bullet + + seventh bullet + + * eighth bullet + \t+ ninth bullet TEXT end @@ -308,36 +392,42 @@ it 'does not nest below defined levels' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - \t\t\ta. fourth bullet - \t\t\t\ti. fifth bullet - \t\t\t\tii. sixth bullet + # Hello there + I. this is the first bullet + \tA. second bullet + \t\t1. third bullet + \t\t\ta. fourth bullet + \t\t\t\ti. fifth bullet + \t\t\t\tii. sixth bullet + \t\t\t\t\t- seventh bullet + \t\t\t\t\t\t* eighth bullet + \t\t\t\t\t\t\t+ ninth bullet TEXT vim.edit filename - vim.normal '6j' - vim.feedkeys 'o' + vim.normal 'GA' + vim.feedkeys '\' vim.feedkeys '\' vim.type 'not a bullet' vim.feedkeys '\' - vim.type 'seventh bullet' + vim.type 'tenth bullet' vim.write file_contents = IO.read(filename) expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - I. this is the first bullet - \tA. second bullet - \t\t1. third bullet - \t\t\ta. fourth bullet - \t\t\t\ti. fifth bullet - \t\t\t\tii. sixth bullet - \t\t\t\t\tnot a bullet - \t\t\t\tiii. seventh bullet + # Hello there + I. this is the first bullet + \tA. second bullet + \t\t1. third bullet + \t\t\ta. fourth bullet + \t\t\t\ti. fifth bullet + \t\t\t\tii. sixth bullet + \t\t\t\t\t- seventh bullet + \t\t\t\t\t\t* eighth bullet + \t\t\t\t\t\t\t+ ninth bullet + \t\t\t\t\t\t\t\tnot a bullet + \t\t\t\t\t\t\t+ tenth bullet TEXT end @@ -345,11 +435,11 @@ it 'removes bullet when promoting top level bullet' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) - # Hello there - A. this is the first bullet + # Hello there + A. this is the first bullet - I. second bullet - \tA. third bullet + I. second bullet + \tA. third bullet TEXT vim.edit filename @@ -363,24 +453,25 @@ file_contents = IO.read(filename) expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - this is the first bullet + # Hello there + this is the first bullet - I. second bullet - third bullet + I. second bullet + third bullet TEXT end - it 'nested outlines handle standard bullets' do + it 'nested outlines handle standard bullets when they are not in outline list' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) - # Hello there - 1. this is the first bullet - \t- standard bullet + # Hello there + 1. this is the first bullet + \t- standard bullet TEXT vim.edit filename + vim.command "let g:bullets_outline_levels=['num','ABC']" vim.normal 'GA' vim.feedkeys '\' vim.type 'second standard bullet' @@ -394,12 +485,12 @@ file_contents = IO.read(filename) expect(file_contents).to eq normalize_string_indent(<<-TEXT) - # Hello there - 1. this is the first bullet - \t- standard bullet - \t- second standard bullet - 2. second bullet - 3. third bullet + # Hello there + 1. this is the first bullet + \t- standard bullet + \t- second standard bullet + 2. second bullet + 3. third bullet TEXT end @@ -443,6 +534,17 @@ vim.type 'seventeenth bullet' vim.feedkeys '\' vim.type 'eighteenth bullet' + vim.feedkeys '\' + vim.type 'ninteenth bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'twentieth bullet' + vim.feedkeys '\' + vim.type 'twenty-first bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'twenty-second bullet' vim.write file_contents = IO.read(filename) @@ -467,6 +569,10 @@ \t\t\ta. sixteenth bullet \t\t\t\ti. seventeenth bullet \t\t\t\tii. eighteenth bullet + \t\t\t\t\t- ninteenth bullet + \t\t\t\t\t- twentieth bullet + \t\t\t\tiii. twenty-first bullet + \t\t\tb. twenty-second bullet TEXT end From 93b33e7176424240abd0545a59b66cf914d33261 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Fri, 6 Mar 2020 18:50:52 -0800 Subject: [PATCH 09/13] support multiple line spacings --- plugin/bullets.vim | 8 ++++++-- spec/nested_bullets_spec.rb | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index 607dc22..c3fdaa6 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -240,7 +240,11 @@ fun! s:closest_bullet_types(from_line_num, max_indent) " DEMO: https://raw.githubusercontent.com/dkarter/bullets.vim/master/img/wrapped-bullets.gif while l:lnum > 1 && (l:curr_indent != 0 || l:bullet_kinds != []) \ && (a:max_indent < l:curr_indent || l:bullet_kinds == []) - let l:lnum = l:lnum - 1 + if l:bullet_kinds != [] + let l:lnum = l:lnum - g:bullets_line_spacing + else + let l:lnum = l:lnum - 1 + endif let l:ltxt = getline(l:lnum) let l:bullet_kinds = s:parse_bullet(l:lnum, l:ltxt) let l:curr_indent = indent(l:lnum) @@ -615,7 +619,7 @@ fun! s:change_bullet_level(direction) if l:curr_bullet != {} " Only change the bullet level if it's currently a bullet. let l:curr_line = l:curr_bullet.starting_at_line_num - let l:closest_bullet = s:closest_bullet_types(l:curr_line - 1, l:curr_indent) + let l:closest_bullet = s:closest_bullet_types(l:curr_line - g:bullets_line_spacing, l:curr_indent) let l:closest_bullet = s:resolve_bullet_type(l:closest_bullet) if l:closest_bullet != {} diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb index 4d28b72..b95e66c 100644 --- a/spec/nested_bullets_spec.rb +++ b/spec/nested_bullets_spec.rb @@ -576,5 +576,43 @@ TEXT end + + it 'adds and changes bullets with multiple line spacing and wrapped lines' do + filename = "#{SecureRandom.hex(6)}.txt" + write_file(filename, <<-TEXT) + # Hello there + I. this is the first bullet + TEXT + + vim.edit filename + vim.command 'let g:bullets_line_spacing=2' + vim.normal 'GA' + vim.feedkeys '\' + vim.type 'second bullet' + vim.feedkeys '\' + vim.feedkeys '\' + vim.type 'third bullet' + vim.feedkeys '\' + vim.normal 'dd' + vim.insert ' wrapped bullet' + vim.feedkeys '\' + vim.type 'fourth bullet' + vim.write + + file_contents = IO.read(filename) + + expect(file_contents).to eq normalize_string_indent(<<-TEXT) + # Hello there + I. this is the first bullet + + II. second bullet + + \tA. third bullet + \twrapped bullet + + \tB. fourth bullet + + TEXT + end end end From 02478f50840118302f18ad2cc741de58bc9b6fd9 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Fri, 6 Mar 2020 19:23:50 -0800 Subject: [PATCH 10/13] fix resolving rom/abc when line spacing > 1 --- plugin/bullets.vim | 4 +- spec/nested_bullets_spec.rb | 93 +++++++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index c3fdaa6..e0a7495 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -276,12 +276,12 @@ endfun " Roman Numeral vs Alphabetic Bullets ---------------------------------- {{{ fun! s:resolve_rom_or_abc(bullet_types) let l:first_type = a:bullet_types[0] - let l:prev_search_starting_line = l:first_type.starting_at_line_num - 1 + let l:prev_search_starting_line = l:first_type.starting_at_line_num - g:bullets_line_spacing let l:bullet_indent = indent(l:first_type.starting_at_line_num) let l:prev_bullet_types = s:closest_bullet_types(l:prev_search_starting_line, l:bullet_indent) while l:prev_bullet_types != [] && l:bullet_indent > indent(l:prev_search_starting_line) - let l:prev_search_starting_line -= 1 + let l:prev_search_starting_line -= g:bullets_line_spacing let l:prev_bullet_types = s:closest_bullet_types(l:prev_search_starting_line, l:bullet_indent) endwhile diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb index b95e66c..173cd72 100644 --- a/spec/nested_bullets_spec.rb +++ b/spec/nested_bullets_spec.rb @@ -500,51 +500,59 @@ write_file(filename, <<-TEXT) # Hello there I. this is the first bullet + \tA. second bullet + \tB. third bullet + \tC. fourth bullet - \tD. fifth bullet - \tE. sixth bullet - \tF. seventh bullet - \tG. eighth bullet - \tH. ninth bullet - \tI. tenth bullet TEXT vim.edit filename + vim.command 'let g:bullets_line_spacing=2' vim.normal 'GA' vim.feedkeys '\' - vim.type 'eleventh bullet' + vim.type 'fifth bullet' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'twelfth bullet' + vim.type 'sixth bullet' vim.feedkeys '\' - vim.type 'thirteenth bullet' + vim.type 'seventh bullet' vim.feedkeys '\' - vim.type 'fourteenth bullet' + vim.type 'eighth bullet' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'fifteenth bullet' + vim.type 'ninth bullet' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'sixteenth bullet' + vim.type 'tenth bullet' vim.feedkeys '\' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'seventeenth bullet' + vim.type 'eleventh bullet' vim.feedkeys '\' - vim.type 'eighteenth bullet' + vim.type 'twelfth bullet' vim.feedkeys '\' - vim.type 'ninteenth bullet' + vim.type 'thirteenth bullet' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'twentieth bullet' + vim.type 'fourteenth bullet' vim.feedkeys '\' - vim.type 'twenty-first bullet' + vim.type 'fifteenth bullet' vim.feedkeys '\' vim.feedkeys '\' vim.feedkeys '\' - vim.type 'twenty-second bullet' + vim.type 'sixteenth bullet' + vim.feedkeys '\' + vim.normal 'dd' + vim.insert ' wrapped line' + vim.feedkeys '\' + vim.type 'seventeenth bullet' + vim.feedkeys '\' + vim.normal 'dd' + vim.insert ' wrapped line' + vim.feedkeys '\' + vim.type 'eighteenth bullet' vim.write file_contents = IO.read(filename) @@ -552,27 +560,42 @@ expect(file_contents).to eq normalize_string_indent(<<-TEXT) # Hello there I. this is the first bullet + \tA. second bullet + \tB. third bullet + \tC. fourth bullet + \tD. fifth bullet - \tE. sixth bullet - \tF. seventh bullet - \tG. eighth bullet - \tH. ninth bullet - \tI. tenth bullet - \tJ. eleventh bullet - II. twelfth bullet - III. thirteenth bullet - \tA. fourteenth bullet - \t\t1. fifteenth bullet - \t\t\ta. sixteenth bullet - \t\t\t\ti. seventeenth bullet - \t\t\t\tii. eighteenth bullet - \t\t\t\t\t- ninteenth bullet - \t\t\t\t\t- twentieth bullet - \t\t\t\tiii. twenty-first bullet - \t\t\tb. twenty-second bullet + + II. sixth bullet + + III. seventh bullet + + \tA. eighth bullet + + \t\t1. ninth bullet + + \t\t\ta. tenth bullet + + \t\t\t\ti. eleventh bullet + + \t\t\t\tii. twelfth bullet + + \t\t\t\t\t- thirteenth bullet + + \t\t\t\t\t- fourteenth bullet + + \t\t\t\tiii. fifteenth bullet + + \t\t\tb. sixteenth bullet + \t\t\t\twrapped line + + \t\t\tc. seventeenth bullet + \t\t\t\twrapped line + + \t\t\td. eighteenth bullet TEXT end From 4e80cc9fc02ba2a9f76858ac935c4e8e8a405161 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Mon, 9 Mar 2020 14:35:06 -0700 Subject: [PATCH 11/13] code cleanup --- plugin/bullets.vim | 162 ++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 69 deletions(-) diff --git a/plugin/bullets.vim b/plugin/bullets.vim index e0a7495..ac17001 100644 --- a/plugin/bullets.vim +++ b/plugin/bullets.vim @@ -616,83 +616,107 @@ fun! s:change_bullet_level(direction) let l:curr_bullet= s:closest_bullet_types(l:lnum, l:curr_indent) let l:curr_bullet = s:resolve_bullet_type(l:curr_bullet) - if l:curr_bullet != {} + if l:curr_bullet == {} " Only change the bullet level if it's currently a bullet. - let l:curr_line = l:curr_bullet.starting_at_line_num - let l:closest_bullet = s:closest_bullet_types(l:curr_line - g:bullets_line_spacing, l:curr_indent) - let l:closest_bullet = s:resolve_bullet_type(l:closest_bullet) - - if l:closest_bullet != {} - let l:islower = l:closest_bullet.bullet ==# tolower(l:closest_bullet.bullet) - let l:closest_type = l:islower ? l:closest_bullet.bullet_type : - \ toupper(l:closest_bullet.bullet_type) - if l:closest_bullet.bullet_type ==# 'std' - let l:closest_type = l:closest_type . l:closest_bullet.bullet - endif - let l:closest_index = index(g:bullets_outline_levels, l:closest_type) - - if l:closest_index >= 0 - let l:closest_indent = indent(l:closest_bullet.starting_at_line_num) - - if (l:curr_indent == l:closest_indent) - let l:next_bullet = s:next_bullet_str(l:closest_bullet) - let l:next_bullet_str = s:pad_to_length(l:next_bullet, l:closest_bullet.bullet_length) - \ . l:curr_bullet.text_after_bullet - - elseif l:closest_index + 1 < len(g:bullets_outline_levels) || l:curr_indent < l:closest_indent - let l:next_index = l:closest_index + 1 - let l:next_type = g:bullets_outline_levels[l:next_index] - let l:next_islower = l:next_type ==# tolower(l:next_type) - let l:trailing_space = ' ' - - let l:curr_bullet.closure = l:closest_bullet.closure - - if l:next_type ==? 'rom' - let l:next_num = s:arabic2roman(1, l:next_islower) - elseif l:next_type ==? 'abc' - let l:next_num = s:dec2abc(1, l:next_islower) - elseif l:next_type ==# 'num' - let l:next_num = '1' - else - " standard bullet; l:next_type contains the bullet symbol to use - let l:next_num = strpart(l:next_type, len(l:next_type) - 1) - let l:curr_bullet.closure = '' - endif - - let l:next_bullet_str = - \ l:curr_bullet.leading_space - \ . l:next_num - \ . l:curr_bullet.closure - \ . l:trailing_space - \ . l:curr_bullet.text_after_bullet - - else - " We're outside of the defined outline levels - let l:next_bullet_str = - \ l:curr_bullet.leading_space - \ . l:curr_bullet.text_after_bullet - endif - - if l:next_bullet_str !=# '' - call setline(l:lnum, l:next_bullet_str) - execute "normal! $" - - else - if g:bullets_delete_last_bullet_if_empty - let l:orig_line = s:parse_bullet(l:lnum, getline(l:lnum)) - if l:orig_line != [] - call setline(l:lnum, l:orig_line[0].leading_space . l:orig_line[0].text_after_bullet) - endif - endif - endif - endif + return + endif + + let l:curr_line = l:curr_bullet.starting_at_line_num + let l:closest_bullet = s:closest_bullet_types(l:curr_line - g:bullets_line_spacing, l:curr_indent) + let l:closest_bullet = s:resolve_bullet_type(l:closest_bullet) + + if l:closest_bullet == {} + " If there is no parent/sibling bullet then this bullet shouldn't change. + return + endif + + let l:islower = l:closest_bullet.bullet ==# tolower(l:closest_bullet.bullet) + let l:closest_type = l:islower ? l:closest_bullet.bullet_type : + \ toupper(l:closest_bullet.bullet_type) + + if l:closest_bullet.bullet_type ==# 'std' + " Append the bullet marker to the type, e.g., 'std*' + + let l:closest_type = l:closest_type . l:closest_bullet.bullet + endif + + let l:closest_index = index(g:bullets_outline_levels, l:closest_type) + + if l:closest_index == -1 + " We are in a list using markers that aren't specified in + " g:bullets_outline_levels so we shouldn't try to change the current + " bullet. + return + endif + + let l:closest_indent = indent(l:closest_bullet.starting_at_line_num) + + if (l:curr_indent == l:closest_indent) + " The closest bullet is a sibling so the current bullet should + " increment to the next bullet marker. + + let l:next_bullet = s:next_bullet_str(l:closest_bullet) + let l:next_bullet_str = s:pad_to_length(l:next_bullet, l:closest_bullet.bullet_length) + \ . l:curr_bullet.text_after_bullet + + elseif l:closest_index + 1 < len(g:bullets_outline_levels) || l:curr_indent < l:closest_indent + " The current bullet is a child of the closest bullet so figure out + " what bullet type it should have and set its marker to the first + " character of that type. + + let l:next_index = l:closest_index + 1 + let l:next_type = g:bullets_outline_levels[l:next_index] + let l:next_islower = l:next_type ==# tolower(l:next_type) + let l:trailing_space = ' ' + + let l:curr_bullet.closure = l:closest_bullet.closure + + " set the bullet marker to the first character of that type + if l:next_type ==? 'rom' + let l:next_num = s:arabic2roman(1, l:next_islower) + elseif l:next_type ==? 'abc' + let l:next_num = s:dec2abc(1, l:next_islower) + elseif l:next_type ==# 'num' + let l:next_num = '1' + else + " standard bullet; l:next_type contains the bullet symbol to use + let l:next_num = strpart(l:next_type, len(l:next_type) - 1) + let l:curr_bullet.closure = '' endif + + let l:next_bullet_str = + \ l:curr_bullet.leading_space + \ . l:next_num + \ . l:curr_bullet.closure + \ . l:trailing_space + \ . l:curr_bullet.text_after_bullet + + else + " We're outside of the defined outline levels + let l:next_bullet_str = + \ l:curr_bullet.leading_space + \ . l:curr_bullet.text_after_bullet endif + + " Apply the new bullet + if l:next_bullet_str !=# '' + call setline(l:lnum, l:next_bullet_str) + execute 'normal! $' + + elseif g:bullets_delete_last_bullet_if_empty + let l:orig_line = s:parse_bullet(l:lnum, getline(l:lnum)) + if l:orig_line != [] + call setline(l:lnum, l:orig_line[0].leading_space . l:orig_line[0].text_after_bullet) + endif + endif + + return endfun fun! s:bullet_demote() call s:change_bullet_level(-1) endfun + fun! s:bullet_promote() call s:change_bullet_level(1) endfun From 75c0e4ffedc602cc6a85ffdb9b4ad40684cfae53 Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Mon, 9 Mar 2020 15:08:22 -0700 Subject: [PATCH 12/13] fix rubocop formatting issues --- spec/alphabetic_bullets_spec.rb | 4 ++-- spec/nested_bullets_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/alphabetic_bullets_spec.rb b/spec/alphabetic_bullets_spec.rb index 854f3ee..74a62cd 100644 --- a/spec/alphabetic_bullets_spec.rb +++ b/spec/alphabetic_bullets_spec.rb @@ -150,11 +150,11 @@ # test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) # # Hello there # a. first bullet might not catch - # me. second line. + # \tme. second line. # INIT # # Hello there # a. first bullet might not catch - # me. second line. + # \tme. second line. # not a bullet # EXPECTED # end diff --git a/spec/nested_bullets_spec.rb b/spec/nested_bullets_spec.rb index 173cd72..5f5f2a7 100644 --- a/spec/nested_bullets_spec.rb +++ b/spec/nested_bullets_spec.rb @@ -462,7 +462,7 @@ TEXT end - it 'nested outlines handle standard bullets when they are not in outline list' do + it 'handle standard bullets when they are not in outline list' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there @@ -600,7 +600,7 @@ TEXT end - it 'adds and changes bullets with multiple line spacing and wrapped lines' do + it 'add and change bullets with multiple line spacing and wrapped lines' do filename = "#{SecureRandom.hex(6)}.txt" write_file(filename, <<-TEXT) # Hello there From d3084b621735a1088e99fcba50a8fc118ed6fc8e Mon Sep 17 00:00:00 2001 From: Keith Miyake Date: Mon, 9 Mar 2020 15:24:21 -0700 Subject: [PATCH 13/13] Add additional TODO test for alpha bullets --- spec/alphabetic_bullets_spec.rb | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/spec/alphabetic_bullets_spec.rb b/spec/alphabetic_bullets_spec.rb index 74a62cd..c175d0d 100644 --- a/spec/alphabetic_bullets_spec.rb +++ b/spec/alphabetic_bullets_spec.rb @@ -143,19 +143,35 @@ EXPECTED end - # it 'does not add a new alpha bullet with wrapped lines' do + # it 'correctly numbers after wrapped lines starting with short words' do # # TODO: maybe take guidance from Pandoc and require two spaces after the # closure to allow us to differentiate between bullets and abbreviations # and words. Might also consider only allowing single letters. - # test_bullet_inserted('not a bullet', <<-INIT, <<-EXPECTED) + # test_bullet_inserted('second bullet', <<-INIT, <<-EXPECTED) # # Hello there # a. first bullet might not catch - # \tme. second line. + # me. second line. # INIT # # Hello there # a. first bullet might not catch # \tme. second line. - # not a bullet + # b. second bullet + # EXPECTED + # end + + # it 'correctly numbers after lines beginning with initialized names' do + # # TODO: maybe take guidance from Pandoc and require two spaces after the + # closure to allow us to differentiate between bullets and abbreviations + # and words. Might also consider only allowing single letters. + # test_bullet_inserted('Second bullet', <<-INIT, <<-EXPECTED) + # # Hello there + # I. The first president of the USA was + # G. Washington. + # INIT + # # Hello there + # I. The first president of the USA was + # G. Washington. + # II. Second bullet # EXPECTED # end