Skip to content

Commit 83fa729

Browse files
authored
fix: do not change selection to update lists (#145)
* Reworked to pass indices * Removed debug echo * Fixed typo * Indent setting by line number * Improved selection preservation * Fixed name collision * Fixed bug in selection code * Calling correct function for indent * Reverted removal of setpos * Optimization to not set selection * Fixed typo * Passing in whether the selection is visual
1 parent 746f92a commit 83fa729

File tree

1 file changed

+91
-68
lines changed

1 file changed

+91
-68
lines changed

plugin/bullets.vim

Lines changed: 91 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,55 @@ fun! s:match_bullet_list_item(input_text)
297297
endfun
298298
" ------------------------------------------------------- }}}
299299

300+
" Selection management ----------------------------------- {{{
301+
302+
" These functions help us maintain the cursor or selection across operation
303+
"
304+
" When getting selection we record
305+
" 1. The start and end line of the selection
306+
" 2. Thd the offset of the start and end column the line end
307+
"
308+
" When setting the selection we set the start and end at the same _offset_
309+
" From the new line start and end. As we manipulate line prefixes, the
310+
" offset from the end represents the correct new cursor position
311+
fun! s:get_selection(is_visual)
312+
let l:sel = {}
313+
let l:mode = a:is_visual ? visualmode() : ''
314+
if l:mode ==# 'v' || l:mode ==# 'V' || l:mode ==# "\<C-v>"
315+
let [l:start_line, l:start_col] = getpos("'<")[1:2]
316+
let l:sel.start_line = l:start_line
317+
let l:sel.start_offset = strlen(getline(sel.start_line)) - l:start_col
318+
let [l:end_line, l:end_col] = getpos("'>")[1:2]
319+
let l:sel.end_line = l:end_line
320+
let l:sel.end_offset = strlen(getline(sel.end_line)) - l:end_col
321+
let l:sel.visual_mode = l:mode
322+
else
323+
let l:sel.start_line = line('.')
324+
let l:sel.start_offset = strlen(getline(sel.start_line)) - col('.')
325+
let l:sel.end_line = l:sel.start_line
326+
let l:sel.end_offset = l:sel.start_offset
327+
let l:sel.visual_mode = ''
328+
endif
329+
return l:sel
330+
endfun
331+
332+
fun! s:set_selection(sel)
333+
let l:start_col = strlen(getline(a:sel.start_line)) - a:sel.start_offset
334+
let l:end_col = strlen(getline(a:sel.end_line)) - a:sel.end_offset
335+
336+
call cursor(a:sel.start_line, l:start_col)
337+
if a:sel.start_line != a:sel.end_line || l:start_col != l:end_col
338+
if a:sel.visual_mode == "\<C-v>"
339+
execute "normal! \<C-v>"
340+
elseif a:sel.visual_mode == 'V' || a:sel.visual_mode == 'v'
341+
execute "normal! v"
342+
endif
343+
call cursor(a:sel.end_line, l:end_col)
344+
endif
345+
endfun
346+
347+
" ------------------------------------------------------- }}}
348+
300349
" Resolve Bullet Type ----------------------------------- {{{
301350
fun! s:closest_bullet_types(from_line_num, max_indent)
302351
let l:lnum = a:from_line_num
@@ -347,7 +396,9 @@ fun! s:find_by_type(bullet_types, type)
347396
return s:find(a:bullet_types, 'v:val.bullet_type ==# "' . a:type . '"')
348397
endfun
349398

350-
" Roman Numeral vs Alphabetic Bullets ---------------------------------- {{{
399+
" --------------------------------------------------------- }}}
400+
401+
" Roman Numeral vs Alphabetic Bullets --------------------- {{{
351402
fun! s:resolve_rom_or_abc(bullet_types)
352403
let l:first_type = a:bullet_types[0]
353404
let l:prev_search_starting_line = l:first_type.starting_at_line_num - g:bullets_line_spacing
@@ -396,7 +447,7 @@ fun! s:has_rom_and_abc(bullet_types)
396447
endfun
397448
" ------------------------------------------------------- }}}
398449

399-
" Checkbox vs Standard Bullets ----------------------------------------- {{{
450+
" Checkbox vs Standard Bullets -------------------------- {{{
400451
fun! s:resolve_chk_or_std(bullet_types)
401452
" if it matches both regular and checkbox it is most likely a checkbox
402453
return s:find_by_type(a:bullet_types, 'chk')
@@ -409,8 +460,6 @@ fun! s:has_chk_and_std(bullet_types)
409460
endfun
410461
" ------------------------------------------------------- }}}
411462

412-
" ------------------------------------------------------- }}}
413-
414463
" Build Next Bullet -------------------------------------- {{{
415464
fun! s:next_bullet_str(bullet)
416465
let l:bullet_type = get(a:bullet, 'bullet_type')
@@ -500,7 +549,7 @@ fun! s:insert_new_bullet()
500549
" indent if previous line ended in a colon
501550
if l:indent_next
502551
" demote the new bullet
503-
call s:change_bullet_level_and_renumber(-1)
552+
call s:change_line_bullet_level(-1, l:next_line_num)
504553
" reset cursor position after indenting
505554
let l:col = strlen(getline(l:next_line_num)) + 1
506555
call setpos('.', [0, l:next_line_num, l:col])
@@ -539,8 +588,6 @@ fun! s:line_ends_in_colon(lnum)
539588
endfun
540589
" --------------------------------------------------------- }}}
541590

542-
" --------------------------------------------------------- }}}
543-
544591
" Checkboxes ---------------------------------------------- {{{
545592
fun! s:find_checkbox_position(lnum)
546593
let l:line_text = getline(a:lnum)
@@ -749,21 +796,26 @@ endfun
749796

750797
" Renumbering --------------------------------------------- {{{
751798
fun! s:renumber_selection()
752-
let l:selection_lines = s:get_visual_selection_lines()
799+
let l:sel = s:get_selection(1)
800+
call s:renumber_lines(l:sel.start_line, l:sel.end_line)
801+
call s:set_selection(l:sel)
802+
endfun
803+
804+
fun! s:renumber_lines(start, end)
753805
let l:prev_indent = -1
754806
let l:levels = {} " stores all the info about the current outline/list
755807

756-
for l:line in l:selection_lines
757-
let l:indent = indent(l:line.nr)
758-
let l:bullet = s:closest_bullet_types(l:line.nr, l:indent)
808+
for l:nr in range(a:start, a:end)
809+
let l:indent = indent(l:nr)
810+
let l:bullet = s:closest_bullet_types(l:nr, l:indent)
759811
let l:bullet = s:resolve_bullet_type(l:bullet)
760812
let l:curr_level = s:get_level(l:bullet)
761813
if l:curr_level > 1
762814
" then it's an AsciiDoc list and shouldn't be renumbered
763815
break
764816
endif
765817

766-
if !empty(l:bullet) && l:bullet.starting_at_line_num == l:line.nr
818+
if !empty(l:bullet) && l:bullet.starting_at_line_num == l:nr
767819
" skip wrapped lines and lines that aren't bullets
768820
if (l:indent > l:prev_indent || !has_key(l:levels, l:indent))
769821
\ && l:bullet.bullet_type !=# 'chk' && l:bullet.bullet_type !=# 'std'
@@ -818,68 +870,54 @@ fun! s:renumber_selection()
818870
let l:renumbered_line = l:bullet.leading_space
819871
\ . l:new_bullet
820872
\ . l:bullet.text_after_bullet
821-
call setline(l:line.nr, l:renumbered_line)
873+
call setline(l:nr, l:renumbered_line)
822874
elseif l:bullet.bullet_type ==# 'chk'
823875
" Reset the checkbox marker if it already exists, or blank otherwise
824876
let l:marker = has_key(l:bullet, 'checkbox_marker') ?
825877
\ l:bullet.checkbox_marker : ' '
826-
call s:set_checkbox(l:line.nr, l:marker)
878+
call s:set_checkbox(l:nr, l:marker)
827879
endif
828880
endif
829881
endfor
830882
endfun
831883

832-
fun! s:renumber_whole_list(...)
833-
" Renumbers the whole list containing the cursor.
834-
" Does not renumber across blank lines.
835-
" Takes 2 optional arguments containing starting and ending cursor positions
836-
" so that we can reset the existing visual selection after renumbering.
884+
" Renumbers the whole list containing the cursor.
885+
fun! s:renumber_whole_list()
837886
let l:first_line = s:first_bullet_line(line('.'))
838887
let l:last_line = s:last_bullet_line(line('.'))
839888
if l:first_line > 0 && l:last_line > 0
840-
" Create a visual selection around the current list so that we can call
841-
" s:renumber_selection() to do the renumbering.
842-
call setpos("'<", [0, l:first_line, 1, 0])
843-
call setpos("'>", [0, l:last_line, 1, 0])
844-
call s:renumber_selection()
845-
if a:0 == 2
846-
" Reset the starting visual selection
847-
call setpos("'<", [0, a:1[0], a:1[1], 0])
848-
call setpos("'>", [0, a:2[0], a:2[1], 0])
849-
execute 'normal! gv'
850-
endif
889+
call s:renumber_lines(l:first_line, l:last_line)
851890
endif
852891
endfun
853892

854893
command! -range=% RenumberSelection call <SID>renumber_selection()
855894
command! RenumberList call <SID>renumber_whole_list()
895+
856896
" --------------------------------------------------------- }}}
857897

858898
" Changing outline level ---------------------------------- {{{
859-
fun! s:change_bullet_level(direction)
860-
let l:lnum = line('.')
861-
let l:curr_line = s:parse_bullet(l:lnum, getline(l:lnum))
899+
fun! s:change_line_bullet_level(direction, lnum)
900+
let l:curr_line = s:parse_bullet(a:lnum, getline(a:lnum))
862901

863902
if a:direction == 1
864-
if l:curr_line != [] && indent(l:lnum) == 0
903+
if l:curr_line != [] && indent(a:lnum) == 0
865904
" Promoting a bullet at the highest level will delete the bullet
866-
call setline(l:lnum, l:curr_line[0].text_after_bullet)
867-
execute 'normal! $'
905+
call setline(a:lnum, l:curr_line[0].text_after_bullet)
868906
return
869907
else
870-
execute 'normal! <<$'
908+
execute a:lnum . 'normal! <<'
871909
endif
872910
else
873-
execute 'normal! >>$'
911+
execute a:lnum . 'normal! >>'
874912
endif
875913

876914
if l:curr_line == []
877915
" If the current line is not a bullet then don't do anything else.
878916
return
879917
endif
880918

881-
let l:curr_indent = indent(l:lnum)
882-
let l:curr_bullet= s:closest_bullet_types(l:lnum, l:curr_indent)
919+
let l:curr_indent = indent(a:lnum)
920+
let l:curr_bullet = s:closest_bullet_types(a:lnum, l:curr_indent)
883921
let l:curr_bullet = s:resolve_bullet_type(l:curr_bullet)
884922

885923
let l:curr_line = l:curr_bullet.starting_at_line_num
@@ -963,42 +1001,27 @@ fun! s:change_bullet_level(direction)
9631001
endif
9641002

9651003
" Apply the new bullet
966-
call setline(l:lnum, l:next_bullet_str)
967-
968-
execute 'normal! $'
969-
return
1004+
call setline(a:lnum, l:next_bullet_str)
9701005
endfun
9711006

972-
fun! s:change_bullet_level_and_renumber(direction)
973-
" Calls change_bullet_level and then renumber_whole_list if required
974-
call s:change_bullet_level(a:direction)
975-
if g:bullets_renumber_on_change
976-
call s:renumber_whole_list()
977-
endif
978-
endfun
9791007

980-
fun! s:visual_change_bullet_level(direction)
1008+
fun! s:change_bullet_level(direction, is_visual)
9811009
" Changes the bullet level for each of the selected lines
982-
let l:start = getpos("'<")[1:2]
983-
let l:end = getpos("'>")[1:2]
984-
let l:selected_lines = range(l:start[0], l:end[0])
985-
for l:lnum in l:selected_lines
986-
" Iterate the cursor position over each line and then call
987-
" s:change_bullet_level for that cursor position.
988-
call setpos('.', [0, l:lnum, 1, 0])
989-
call s:change_bullet_level(a:direction)
1010+
let l:sel = s:get_selection(a:is_visual)
1011+
for l:lnum in range(l:sel.start_line, l:sel.end_line)
1012+
call s:change_line_bullet_level(a:direction, l:lnum)
9901013
endfor
1014+
9911015
if g:bullets_renumber_on_change
992-
" Pass the current visual selection so that it gets reset after
993-
" renumbering the list.
994-
call s:renumber_whole_list(l:start, l:end)
1016+
call s:renumber_whole_list()
9951017
endif
1018+
call s:set_selection(l:sel)
9961019
endfun
9971020

998-
command! BulletDemote call <SID>change_bullet_level_and_renumber(-1)
999-
command! BulletPromote call <SID>change_bullet_level_and_renumber(1)
1000-
command! -range=% BulletDemoteVisual call <SID>visual_change_bullet_level(-1)
1001-
command! -range=% BulletPromoteVisual call <SID>visual_change_bullet_level(1)
1021+
command! BulletDemote call <SID>change_bullet_level(-1, 0)
1022+
command! BulletPromote call <SID>change_bullet_level(1, 0)
1023+
command! -range=% BulletDemoteVisual call <SID>change_bullet_level(-1, 1)
1024+
command! -range=% BulletPromoteVisual call <SID>change_bullet_level(1, 1)
10021025

10031026
" --------------------------------------------------------- }}}
10041027

@@ -1089,7 +1112,7 @@ fun! s:get_visual_selection_lines()
10891112
let l:index = l:lnum1
10901113
let l:lines_with_index = []
10911114
for l:line in l:lines
1092-
let l:lines_with_index += [{'text': l:line, 'nr': l:index}]
1115+
call add(l:lines_with_index, {'text': l:line, 'nr': l:index})
10931116
let l:index += 1
10941117
endfor
10951118
return l:lines_with_index

0 commit comments

Comments
 (0)