" vis.vim:  Perform an Ex command on a visual highlighted block (CTRL-V).
" Date:			Sep 08, 2016 - Dec 08, 2021
" Version:		23a	ASTRO-ONLY
" Maintainer:	Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM>
" GetLatestVimScripts: 1066 1 cecutil.vim
" GetLatestVimScripts: 1195 1 :AutoInstall: vis.vim
" Verse: For am I now seeking the favor of men, or of God? Or am I striving
" to please men? For if I were still pleasing men, I wouldn't be a servant
" of Christ. (Gal 1:10, WEB)
"
"           Based on an idea of Stefan Roemer <roemer@informatik.tu-muenchen.de>
"
" ------------------------------------------------------------------------------
" Initialization: {{{1
" Exit quickly when <Vis.vim> has already been loaded or
" when 'compatible' is set
if &cp || exists("g:loaded_vis")
  finish
endif
let s:keepcpo    = &cpo
let g:loaded_vis = "v23a"
set cpo&vim
"DechoRemOn

" =====================================================================
"  Support Functions: {{{1

" ---------------------------------------------------------------------
" vis#VisBlockCmd: {{{2
fun! vis#VisBlockCmd(cmd) range
"  call Dfunc("vis#VisBlockCmd(cmd<".a:cmd.">")

  " retain and re-use same visual mode
  sil! keepj norm `<
  let curposn = SaveWinPosn(0)
  let vmode   = visualmode()
"  call Decho("vmode<".vmode.">")

  call s:SaveUserSettings()

  if vmode == 'V'
"   call Decho("handling V mode")
"   call Decho("cmd<".a:cmd.">")
   exe "keepj '<,'>".a:cmd

  else " handle v and ctrl-v
"   call Decho("handling v or ctrl-v mode")
   " Initialize so (begcol < endcol) for non-v modes
   let begcol   = s:VirtcolM1("<")
   let endcol   = s:VirtcolM1(">")
   if vmode != 'v'
    if begcol > endcol
     let begcol = s:VirtcolM1(">")
     let endcol = s:VirtcolM1("<")
    endif
   endif

   " Initialize so that begline<endline
   let begline  = a:firstline
   let endline  = a:lastline
   if begline > endline
    let begline = a:lastline
    let endline = a:firstline
   endif
"   call Decho('beg['.begline.','.begcol.'] end['.endline.','.endcol.']')

   " =======================
   " Modify Selected Region:
   " =======================
   " 1. delete selected region into register "a
"   call Decho("1. delete selected region into register a")
   sil! keepj norm! gv"ad
"   call Decho("1. reg-A<".@a.">")
"   call Decho("Step#1: deleted selected region into register")|redraw!|sleep 3	" Decho

   " 2. put cut-out text at end-of-file
"   call Decho("2. put cut-out text at end-of-file")
   keepj $
   keepj pu_
   let lastline= line("$")
   sil! keepj norm! "aP
"   call Decho("2. reg-A<".@a.">")
"   call Decho("Step#2: put text at end-of-file")|redraw!|sleep 3	" Decho

   " 3. apply command to those lines
   let curline = line(".")
   ka
   keepj $
"   call Decho("3. apply command<".a:cmd."> to those lines (curline=".line(".").")")
   exe "keepj ". curline.',$'.a:cmd
"   call Decho("Step#3: apply command")|redraw!|sleep 3	" Decho

   " 4. Prepend the "empty_chr" since "ad on empty lines inserts blanks
   if exists("g:vis_empty_character")
	let empty_chr= g:vis_empty_character
   else
    let empty_chr= (&enc == "euc-jp")? "\<Char-0x01>" : "\<Char-0xff>"
   endif
   " if the command removes the text, then don't do anything with the
   " non-existent text (for example, :B !true  under unix)
   if curline <= line("$")
	exe "keepj sil! ". curline.',$s/^/'.empty_chr.'/'
"	call Decho("Step#3a: prepend empty-character")|redraw!|sleep 3	" Decho

	" 5. visual-block select the modified text in those lines
"	call Decho("5. visual-block select modified text at end-of-file")
	exe "keepj ".lastline
	exe 'keepj norm! 0'.vmode.'G$"ax'
	if @a[strlen(@a)-1] == ' '
	 " nasty fix -- sometimes the 0G$"ax picks up the end-of-line as a space
	 let @a= strpart(@a,0,strlen(@a)-1)
	endif
"	call Decho("5. reg-A<".@a.">")
"	call Decho("Step#5: select modified text")|redraw!|sleep 3	" Decho

	" 6. delete excess lines
"	call Decho("6. delete excess lines")
	exe "sil! keepj ".lastline.',$d'
"	call Decho("Step#6: delete excess lines")|redraw!|sleep 3	" Decho

	" 7. put modified text back into file
"	call Decho("7. put modifed text back into file (beginning=".begline.".".begcol.")")
	exe "keepj ".begline
	if begcol > 1
	 exe 'sil! keepj norm! '.begcol."\<bar>\"ap"
	elseif begcol == 1
	 norm! 0"ap
	else
	 norm! 0"aP
	endif
"	call Decho("Step#7: put modified text back")|redraw!|sleep 3	" Decho
   endif

   " 8. attempt to restore gv -- this is limited, it will
   "    select the same size region in the same place as before,
   "    not necessarily the changed region
"   call Decho("8. restore gv")
   let begcol= begcol+1
   let endcol= endcol+1
   exe "sil keepj ".begline
   exe 'sil keepj norm! '.begcol."\<bar>".vmode
   exe "sil keepj ".endline
   exe 'sil keepj norm! '.endcol."\<bar>\<esc>"
   exe "sil keepj ".begline
   exe 'sil keepj norm! '.begcol."\<bar>"
"   call Decho("Step#8: restore gv")|redraw!|sleep 3	" Decho

   " 9. Remove empty-character from text
"   call Decho("9. remove empty-character from lines ".begline." to ".endline)
   exe "sil! keepj ".begline.','.endline.'s/'.empty_chr.'//e'
"   call Decho("Step#9: remove empty-character")|redraw!|sleep 3	" Decho
  endif

  " restore a-priori condition
  call s:RestoreUserSettings()
  call RestoreWinPosn(curposn)
"  call Decho("Step#10: done!")|redraw!|sleep 3	" Decho

"  call Dret("vis#VisBlockCmd")
endfun

" ---------------------------------------------------------------------
" vis#VisBlockSearch: {{{2
fun! vis#VisBlockSearch(...) range
"  call Dfunc("vis#VisBlockSearch() a:0=".a:0." lines[".a:firstline.",".a:lastline."]")
  let keepic= &ic
  set noic

  if a:0 >= 1 && strlen(a:1) > 0
   let pattern   = a:1
   let s:pattern = pattern
"   call Decho("a:0=".a:0.": pattern<".pattern.">")
  elseif exists("s:pattern")
   let pattern= s:pattern
  else
   let pattern   = @/
   let s:pattern = pattern
  endif
  let vmode= visualmode()

  " collect search restrictions
  let firstline  = line("'<")
  let lastline   = line("'>")
  let firstcolm1 = s:VirtcolM1("<")
  let lastcolm1  = s:VirtcolM1(">")
"  call Decho("1: firstline=".firstline." lastline=".lastline." firstcolm1=".firstcolm1." lastcolm1=".lastcolm1)

  if(firstline > lastline)
   let firstline = line("'>")
   let lastline  = line("'<")
   if a:0 >= 1
    keepj norm! `>
   endif
  else
   if a:0 >= 1
    keepj norm! `<
   endif
  endif
"  call Decho("2: firstline=".firstline." lastline=".lastline." firstcolm1=".firstcolm1." lastcolm1=".lastcolm1)

  if vmode != 'v'
   if firstcolm1 > lastcolm1
   	let tmp        = firstcolm1
   	let firstcolm1 = lastcolm1
   	let lastcolm1  = tmp
   endif
  endif
"  call Decho("3: firstline=".firstline." lastline=".lastline." firstcolm1=".firstcolm1." lastcolm1=".lastcolm1)

  let firstlinem1 = firstline  - 1
  let lastlinep1  = lastline   + 1
  let firstcol    = firstcolm1 + 1
  let lastcol     = lastcolm1  + 1
  let lastcolp1   = lastcol    + 1
"  call Decho("4: firstline=".firstline." lastline=".lastline." firstcolm1=".firstcolm1." lastcolp1=".lastcolp1)

  " construct search string
  if vmode == 'V'
   let srch= '\%(\%>'.firstlinem1.'l\%<'.lastlinep1.'l\)\&'
"   call Decho("V  srch: ".srch)
  elseif vmode == 'v'
   if firstline == lastline || firstline == lastlinep1
   	let srch= '\%(\%'.firstline.'l\%>'.firstcolm1.'v\%<'.lastcolp1.'v\)\&'
   else
    let srch= '\%(\%(\%'.firstline.'l\%>'.firstcolm1.'v\)\|\%(\%'.lastline.'l\%<'.lastcolp1.'v\)\|\%(\%>'.firstline.'l\%<'.lastline.'l\)\)\&'
   endif
"   call Decho("v  srch: ".srch)
  else
   let srch= '\%(\%>'.firstlinem1.'l\%>'.firstcolm1.'v\%<'.lastlinep1.'l\%<'.lastcolp1.'v\)\&'
"   call Decho("^v srch: ".srch)
  endif

  " perform search
  if a:0 <= 1
"   call Decho("Search forward: <".srch.pattern.">")
   call search(srch.pattern)
   let @/= srch.pattern

  elseif a:0 == 2
"   call Decho("Search backward: <".srch.pattern.">")
   call search(srch.pattern,a:2)
   let @/= srch.pattern
  endif

  " restore ignorecase
  let &ic= keepic

"  call Dret("vis#VisBlockSearch <".srch.">")
  return srch
endfun

" ---------------------------------------------------------------------
" vis#VisFmt: {{{2
fun! vis#VisFmt() range
"  call Dfunc("vis#VisFmt()")
   if exists("g:vis_empty_character")
	let empty_chr= g:vis_empty_character
   else
    let empty_chr= (&enc == "euc-jp")? "\<Char-0x01>" : "\<Char-0xff>"
   endif

  norm! gv
  '<,'>call vis#VisBlockCmd('norm! gqip}}')
  let lineno= line("'>")+1
  exe lineno
  while getline(".") =~ empty_chr
   exe 's/'.empty_chr.'//'
   norm! j
  endwhile
"  call Dret("vis#VisFmt")
endfun

" ---------------------------------------------------------------------
" s:VirtcolM1: usually a virtcol(mark)-1, but due to tabs this can {{{2
"              be different
fun! s:VirtcolM1(mark)
"  call Dfunc('s:VirtcolM1("'.a:mark.'")')

  if virtcol("'".a:mark) <= 1
"   call Dret("s:VirtcolM1 0")
   return 0
  endif

  if &ve == "block"
   " Works around a ve=all vs ve=block difference with virtcol().
   " Since s:SaveUserSettings() changes ve to ve=all, this small
   " ve override only affects vis#VisBlockSearch().
   set ve=all
"   call Decho("temporarily setting ve=all")
  endif

"  call Decho("exe norm! `".a:mark."h")
  exe "keepj norm! `".a:mark."h"

  let vekeep = &ve
  let vc     = virtcol(".")
  let &ve    = vekeep

"  call Dret("s:VirtcolM1 ".vc)
  return vc
endfun

" ---------------------------------------------------------------------
" s:SaveUserSettings: save options which otherwise may interfere {{{2
fun! s:SaveUserSettings()
"  call Dfunc("s:SaveUserSettings()")
  let s:keep_lz    = &lz
  let s:keep_fen   = &fen
  let s:keep_fo    = &fo
  let s:keep_ic    = &ic
  let s:keep_magic = &magic
  let s:keep_sol   = &sol
  let s:keep_ve    = &ve
  let s:keep_ww    = &ww
  let s:keep_cedit = &cedit
  set lz magic nofen noic nosol ww= fo=nroql2 cedit&
  " determine whether or not "ragged right" is in effect for the selected region
  let raggedright= 0
  norm! `>
  let rrcol = col(".")
  while line(".") >= line("'<")
"   call Decho(".line#".line(".").": col(.)=".col('.')." rrcol=".rrcol)
   if col(".") != rrcol
    let raggedright = 1
	break
   endif
   if line(".") == 1
	break
   endif
   norm! k
  endwhile
  if raggedright
"   call Decho("ragged right: set ve=all")
   set ve=all
  else
"   call Decho("smooth right: set ve=")
   set ve=
  endif

  " Save any contents in register a
  let s:rega= @a

"  call Dret("s:SaveUserSettings")
endfun

" ---------------------------------------------------------------------
" s:RestoreUserSettings: restore register a and options {{{2
fun! s:RestoreUserSettings()
"  call Dfunc("s:RestoreUserSettings()")
  let @a     = s:rega
  let &cedit = s:keep_cedit
  let &fen   = s:keep_fen
  let &fo    = s:keep_fo
  let &ic    = s:keep_ic
  let &lz    = s:keep_lz
  let &sol   = s:keep_sol
  let &ve    = s:keep_ve
  let &ww    = s:keep_ww
"  call Dret("s:RestoreUserSettings")
endfun

let &cpo= s:keepcpo
unlet s:keepcpo
" ------------------------------------------------------------------------------
"  Modelines: {{{1
" vim: fdm=marker
