Skip to content

Commit

Permalink
Add highlightedyank#highlight#add() function
Browse files Browse the repository at this point in the history
Let the highlight function independent and bring it to public.
The highlight function itself is responsible for the time-out.
  • Loading branch information
machakann committed Oct 2, 2018
1 parent ac2eeb8 commit 26a2ecc
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 218 deletions.
151 changes: 54 additions & 97 deletions autoload/highlightedyank.vim
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
" highlighted-yank: Make the yanked region apparent!
" FIXME: Highlight region is incorrect when an input ^V[count]l ranges
" multiple lines.
let s:Schedule = vital#highlightedyank#new().import('Schedule')
\.augroup('highlightedyank-highlight')
let s:NULLPOS = [0, 0, 0, 0]
let s:NULLREGION = {
\ 'wise': '', 'blockwidth': 0,
\ 'head': copy(s:NULLPOS), 'tail': copy(s:NULLPOS),
\ }
let s:NULLREGION = [s:NULLPOS, s:NULLPOS, '']
let s:MAXCOL = 2147483647
let s:ON = 1
let s:OFF = 0
Expand All @@ -17,6 +12,7 @@ let s:HIGROUP = 'HighlightedyankRegion'

let s:timer = -1

" Highlight the yanked region
function! highlightedyank#debounce() abort "{{{
if s:state is s:OFF
return
Expand All @@ -30,11 +26,10 @@ function! highlightedyank#debounce() abort "{{{
call timer_stop(s:timer)
endif

" NOTE: The timer callback would not be called while vim is busy, thus the
" highlight procedure starts after the control has been returned to
" user.
" NOTE: The timer callback is not called while vim is busy, thus the
" highlight procedure starts after the control is returned to the user.
" This makes complex-repeat faster because the highlight doesn't
" performed in a macro execution.
" performed during a macro execution.
let s:timer = timer_start(1, {-> s:highlight(operator, regtype, regcontents, marks)})
endfunction "}}}

Expand Down Expand Up @@ -62,16 +57,20 @@ endfunction "}}}

function! s:highlight(operator, regtype, regcontents, marks) abort "{{{
let s:timer = -1
if a:operator !=# 'y' || a:regtype ==# ''
return
endif
if a:marks !=# [line("'["), line("']"), col("'["), col("']")]
return
endif
if a:operator !=# 'y' || a:regtype ==# ''

let [start, end, type] = s:get_region(a:regtype, a:regcontents)
if empty(type)
return
endif

let region = s:derive_region(a:regtype, a:regcontents)
let maxlinenumber = s:get('max_lines', 10000)
if region.tail[1] - region.head[1] + 1 > maxlinenumber
if end[1] - start[1] + 1 > maxlinenumber
return
endif

Expand All @@ -80,145 +79,103 @@ function! s:highlight(operator, regtype, regcontents, marks) abort "{{{
return
endif

call s:glow(region, s:HIGROUP, hi_duration)
call highlightedyank#highlight#add(s:HIGROUP, start, end, type, hi_duration)
endfunction "}}}


function! s:derive_region(regtype, regcontents) abort "{{{
function! s:get_region(regtype, regcontents) abort "{{{
if a:regtype ==# 'v'
let region = s:derive_region_char(a:regcontents)
return s:get_region_char(a:regcontents)
elseif a:regtype ==# 'V'
let region = s:derive_region_line(a:regcontents)
return s:get_region_line(a:regcontents)
elseif a:regtype[0] ==# "\<C-v>"
" NOTE: the width from v:event.regtype is not correct if 'clipboard' is
" unnamed or unnamedplus in windows
" let width = str2nr(a:regtype[1:])
let region = s:derive_region_block(a:regcontents)
else
let region = deepcopy(s:NULLREGION)
return s:get_region_block(a:regcontents)
endif
return region
return s:NULLREGION
endfunction "}}}


function! s:derive_region_char(regcontents) abort "{{{
function! s:get_region_char(regcontents) abort "{{{
let len = len(a:regcontents)
let region = {}
let region.wise = 'char'
let region.head = getpos("'[")
let region.tail = copy(region.head)
let start = getpos("'[")
let end = copy(start)
if len == 0
let region = deepcopy(s:NULLREGION)
return s:NULLREGION
elseif len == 1
let region.tail[2] += strlen(a:regcontents[0]) - 1
let end[2] += strlen(a:regcontents[0]) - 1
elseif len == 2 && empty(a:regcontents[1])
let region.tail[2] += strlen(a:regcontents[0])
let end[2] += strlen(a:regcontents[0])
else
if empty(a:regcontents[-1])
let region.tail[1] += len - 2
let region.tail[2] = strlen(a:regcontents[-2])
let end[1] += len - 2
let end[2] = strlen(a:regcontents[-2])
else
let region.tail[1] += len - 1
let region.tail[2] = strlen(a:regcontents[-1])
let end[1] += len - 1
let end[2] = strlen(a:regcontents[-1])
endif
endif
return s:modify_region(region)
let end = s:modify_end(end)
return [start, end, 'v']
endfunction "}}}


function! s:derive_region_line(regcontents) abort "{{{
let region = {}
let region.wise = 'line'
let region.head = getpos("'[")
let region.tail = getpos("']")
if region.tail[2] == s:MAXCOL
let region.tail[2] = col([region.tail[1], '$'])
function! s:get_region_line(regcontents) abort "{{{
let start = getpos("'[")
let end = getpos("']")
if end[2] == s:MAXCOL
let end[2] = col([end[1], '$'])
endif
return region
return [start, end, 'V']
endfunction "}}}


function! s:derive_region_block(regcontents) abort "{{{
function! s:get_region_block(regcontents) abort "{{{
let len = len(a:regcontents)
if len == 0
return deepcopy(s:NULLREGION)
return s:NULLREGION
endif

let view = winsaveview()
let curcol = col('.')
let width = max(map(copy(a:regcontents), 'strdisplaywidth(v:val, curcol)'))
let region = deepcopy(s:NULLREGION)
let region.wise = 'block'
let region.head = getpos("'[")
call setpos('.', region.head)
let start = getpos("'[")
call setpos('.', start)
if len > 1
execute printf('normal! %sj', len - 1)
endif
execute printf('normal! %s|', virtcol('.') + width - 1)
let region.tail = getpos('.')
let region.blockwidth = width
let end = s:modify_end(getpos('.'))
call winrestview(view)

let blockwidth = width
if strdisplaywidth(getline('.')) < width
let region.blockwidth = s:MAXCOL
let blockwidth = s:MAXCOL
endif
call winrestview(view)
return s:modify_region(region)
let type = "\<C-v>" . blockwidth
return [start, end, type]
endfunction "}}}


function! s:modify_region(region) abort "{{{
function! s:modify_end(end) abort "{{{
" for multibyte characters
if a:region.tail[2] == col([a:region.tail[1], '$']) || a:region.tail[3] != 0
return a:region
if a:end[2] == col([a:end[1], '$']) || a:end[3] != 0
return a:end
endif

let cursor = getpos('.')
call setpos('.', a:region.tail)
call setpos('.', a:end)
let letterhead = searchpos('\zs', 'bcn', line('.'))
if letterhead[1] > a:region.tail[2]
if letterhead[1] > a:end[2]
" try again without 'c' flag if letterhead is behind the original
" position. It may look strange but it happens with &enc ==# 'cp932'
let letterhead = searchpos('\zs', 'bn', line('.'))
endif
let a:region.tail[1:2] = letterhead
let a:end[1:2] = letterhead
call setpos('.', cursor)
return a:region
endfunction "}}}


let s:quenchtask = {}

function! s:glow(region, hi_group, duration) abort "{{{
let highlight = highlightedyank#highlight#new(a:hi_group, a:region)
if empty(highlight)
return
endif

if !empty(s:quenchtask) && !s:quenchtask.hasdone()
call s:quenchtask.trigger()
endif
let succeeded = highlight.show()
if !succeeded
return
endif
if !has('patch-8.0.1476') && has('patch-8.0.1449')
redraw
endif

let switchtask = s:Schedule.Task()
\.call(highlight.switch, [], highlight)
\.repeat(-1)
\.waitfor(['BufEnter'])

let s:quenchtask = s:Schedule.Task()
\.call(highlight.quench, [], highlight)
\.call(switchtask.cancel, [], switchtask)
\.repeat(1)
\.waitfor([a:duration,
\ ['TextChanged', '<buffer>'],
\ ['InsertEnter', '<buffer>'],
\ ['BufUnload', '<buffer>'],
\ ['CmdwinLeave', '<buffer>'],
\ ['TabLeave', '*']])
return a:end
endfunction "}}}


Expand Down
Loading

0 comments on commit 26a2ecc

Please sign in to comment.