1
0
Fork 0
mirror of synced 2024-11-26 18:55:34 -05:00
ultimate-vim/vim_plugin_candinates_src/sketch-0-3-2/sketch.vim

1064 lines
37 KiB
VimL
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

" File: sketch.vim
" Version: 0.3.2
" Date: 2004-01-18
" Author: Antony Scriven <ads@metawire.org>
" Maintainer: Antony Scriven <ads@metawire.org>
" Description: Line drawing, drag and drop, tables, arrows, boxes, fills,
" using the mouse.
"
" Tutorial version: 2003-10-02
"
" Usage and settings
" ------------------
" See the file sketch.tut for a tutorial. There isn't a help file yet.
"
"
" Change or uncomment this line so you can start or stop Sketch.
" noremap <F1> :call ToggleSketch()<CR>
"
" Don't have a meta/alt key? Use this mapping to switch between square corners
" and rounded corners for boxes/lines
" nnoremap <buffer> <silent> <AnyKeyYouLike> <LeftMouse>:call NextCornerStyle()<CR>
"
" Release notes
" -------------
" 0.3.2 - Tabs still caused garbled screen when dragging transparent
" objects. Fixed.
" 0.3.1 - cursor() can cause problems in a file with tabs when using
" virutal edit mode. Changed to use G and |. Many tutorial
" errors fixed.
" 0.3.0 - Flood fill with arbitrary character added.
" 0.2.3 - Bug 6 fixed.
" 0.2.2 - Error in the tutorial that I thought I'd fixed is now really
" fixed. Thanks to pinkie..
" - Tutorial updated.
" 0.2.1 - Bug 9 fixed.
" - Tutorial updated.
" - Keys rearranged again, sorry. Most important are the line
" drawing actions. These are all accessible via shift and
" control keys. Meta is used for painting/fat erase. This
" shouldn't be much of a loss if your terminal/keyboard
" doesn't have or doesn't like the meta/alt key.
" 0.2.0 - Bug 3 fixed.
" - Bug 1 fixed.
" - Bug 7 fixed.
" - Bug 5 fixed.
" - Corrected tutorial errors.
" Thanks to Tijs Van Den Bogaard.
" Added a quick reference card.
" - Changed some commands around slightly after
" encountering some usability problems:
" Shift+LeftMouse is now paint.
" Control+LeftMouse is brush select.
" Shift+RightMouse is erase.
" Control+RightMouse is fat erase.
" - Vis select then line/box/fill sets the arrowhead.
" - Drag/drop opaque & transparent.
" - Lines/boxes can have round corners: <M-LeftMouse>
" 0.1.1 Bug 2 fixed.
" 0.1.0 Initial beta release
"
"
" Please don't tell me my code is horrible, I know it is. It started as a
" quick test and I've hacked on all sorts of features in a nasty nasty way.
" Using virtualedit seems buggy, or at least how it works isn't entirely
" obvious in certain situations. So I've hacked in some workarounds.
" I'll tidy it up later (honest).
"
" To do, in order of priority (notes and examples at the end of the file)
" -----------------------------------------------------------------------
" - make a better behaved plugin
" <SID> etc
" uses registers in a few places withouth documenting this. Need a scheme to
" avoid this.
" - use the new setreg features if ver = vim6.2
" - vim help file with command reference and link to tutorial
" - Rewrite the code nicely :-)
" >>Version 1
" - diagonals
" - coarser drawing to make straight lines and diagonals easier
" - keyboard support
" ...
" - painting with colours! (horrendously slow last I checked)
"
" Known bugs
" ----------
" 10. Transparent drag doesn't quite work for dragging to column 1.
" 4. Try drawing from below the bottom of the file. I don't think this is
" fixable. But it may be possible to make it react a little more sanely.
" 8. When dragging an object, doesn't quite work right when you are one
" column to the right of your starting point.
" Has this gone away?
"
" Fixed bugs
" ----------
" 9. If mousemodel=popup then rightrelease gives an annoying menu.
" --Solution: save &mousemodel; let mousemodel=''
" 5. Select a line, then left-click to draw it. Now if you
" double-click the arrow is in the correct direction. However, if
" you select a line, then double-click to draw a line+arrow, the
" arrowhead is not in the correct direction.
" -- Fixed by getting rightrelease to handle this.
" 6. Right vertical line of a visual box is drawn one
" column to the right. Happens if the last cursor position of the visual
" selection is on an empty line, and is to the right of the starting
" position.
" 7. Drawing through, e.g., letters produces '+'s. This should be lines.
" 3. Visual selection, then click in another window: the click is
" interpreted with the buffer-local map of the window you are
" drawing in and you get a lot of error messages. This should be
" fixable. (However not for erase :-()
" 1. filling a box: rarely the leftmost, uppermost `-' isn't drawn.
" Hopefully fixed by fixing bug 2!
" 2. very rarely the left vertical line of a visual box is drawn one
" column to the right ?? (virtualedit problem or my strlen()
" problem). Happens if the last cursor position of the visual
" selection is on an empty line, and is to the right of the
" starting position.
"
"
" ----------------------------------------------------------------------
" To follow: poorly named, poorly commented, poorly written functions in
" a random order. Sorry.
fun! Cursor(line,col)
exe 'silent norm! ' . a:line . 'G' . a:col . '|'
endfun
fun! ToggleSketch()
if !exists("b:Sketch_loaded")
let b:Sketch_loaded = 1
call Sketch()
echo '[Sketch on]'
norm! zRz.
else
unlet b:Sketch_loaded
call NoSketch()
echo '[Sketch off]'
endif
endfun
fun! NextBrush()
let b:brushes = strpart(b:brushes,1,strlen(b:brushes)-1).b:brushes[0]
echo '['.b:brushes[0].']['.((b:roundcorners==1)?'Round':'Square').']'
endfun
fun! NextCornerStyle()
if b:roundcorners == 1
let b:roundcorners = 0
else
let b:roundcorners = 1
endif
echo '['.b:brushes[0].']['.((b:roundcorners==1)?'Round':'Square').']'
endfun
fun! MarkVisStart()
call SketchSavePos('auto')
let b:Vis_start_line = line(".")
let b:Vis_start_col = virtcol(".")
let g:Sketch_winnr = winnr()
endfun
fun! MarkVisEnd()
"Fixes bug 3. Clicking outside of the window where you first made the
"selection.
if winnr() != g:Sketch_winnr
return 'abort'
endif
normal! gv
let b:Vis_end_line = line(".")
let b:Vis_end_col = virtcol(".")
normal! 
return ''
endfun
fun! Sketch()
let b:brushes = '.:#'
let b:Sketch_dir2 = 0
let b:Sketch_dir = 'r'
let b:Sketch_line = 0
let b:Sketch_col = 0
let b:Sketch_savedguiopts = &go
let b:Sketch_savedmousemodel = &mousemodel
set mousemodel=
let b:Vis_start_line = 0
let b:Vis_start_col = 0
let b:Vis_end_line = 0
let b:Vis_end_col = 0
let b:Sketch_erasesize = 1
let g:Sketch_winnr = winnr()
let b:roundcorners = 0
set go+=rb
setl ve=all
"Lines
nnoremap <buffer> <silent> <LeftMouse> <LeftMouse>:call SketchClick('-')<CR>
nnoremap <buffer> <silent> <LeftDrag> <LeftMouse>:call SketchDrag()<CR>
"Arrow
nnoremap <buffer> <silent> <2-LeftMouse> :call SketchClick('>')<CR>
"Single vert bar
nnoremap <buffer> <silent> <3-LeftMouse> :call SketchClick('\|')<CR>
"Painting
nnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse>:call SketchPaint()<CR>
nnoremap <buffer> <silent> <C-LeftDrag> <LeftMouse>:call SketchPaint()<CR>
nnoremap <buffer> <silent> <M-LeftMouse> <LeftMouse>:call NextBrush()<CR>
nnoremap <buffer> <silent> <M-2-LeftMouse> <LeftMouse>:call NextBrush()<CR>
nnoremap <buffer> <silent> <M-3-LeftMouse> <LeftMouse>:call NextBrush()<CR>
nnoremap <buffer> <silent> <M-4-LeftMouse> <LeftMouse>:call NextBrush()<CR>
"Selection
nnoremap <buffer> <silent> <RightMouse> <LeftMouse>:call MarkVisStart()<CR><4-LeftMouse>
vnoremap <buffer> <silent> <RightDrag> <LeftDrag>
vnoremap <buffer> <silent> <RightRelease> <LeftMouse>:call SketchSavePos('auto')<CR>gv
"The gv"ap at the end prevents flicker when M-RightDrag does the undo.
vnoremap <buffer> <silent> <S-RightMouse> <LeftMouse>:call VisSaveDims()<CR>gv"pygv"pp:let b:above = @p<CR>
nnoremap <buffer> <silent> <S-RightDrag> u<LeftMouse>:call VisWithSavedDims()<CR>"pp
nnoremap <buffer> <silent> <RightDrag> u<LeftMouse>:call VisWithSavedDims()<CR>:call PasteTransparent()<CR>:call VisWithSavedDims()<CR>"pp
"Erase
nnoremap <buffer> <silent> <C-RightMouse> <LeftMouse>:call SketchErase('small')<CR>
nnoremap <buffer> <silent> <C-RightDrag> <LeftMouse>:call SketchErase('small')<CR>
nnoremap <buffer> <silent> <M-RightMouse> <LeftMouse>:call SketchErase('big')<CR>
nnoremap <buffer> <silent> <M-RightDrag> <LeftMouse>:call SketchErase('big')<CR>
vnoremap <buffer> <silent> <RightMouse> <LeftMouse>:call VisSaveDims()<CR>gv"pygv"pp:let b:above = @p<CR>gv:call SketchErase('vblock')<CR><LeftMouse>
" vnoremap <buffer> <silent> <RightMouse> <LeftMouse>:call VisSaveDims()<CR>gv"py:let b:above = @p<CR>gv:call SketchErase('vblock')<CR><LeftMouse>
vnoremap <buffer> <silent> <2-RightMouse> <LeftMouse>:call VisSaveDims()<CR>gv"pygv"pp:let b:above = @p<CR>gv:call SketchErase('vblock')<CR><LeftMouse>
"vnoremap <buffer> <silent> <RightMouse> :call SketchErase('vblock')<CR><LeftMouse>
"Fill
vnoremap <buffer> <silent> <LeftMouse> <LeftMouse>:call SketchFillBox('transparent')<CR>
vnoremap <buffer> <silent> <S-LeftMouse> <LeftMouse>:call SketchFillBox('opaque')<CR>
vnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse>:call SketchFillBrush('opaque')<CR>
nnoremap <buffer> <silent> <S-LeftMouse> <LeftMouse>:call NextCornerStyle()<CR>
nnoremap <buffer> <silent> <S-2-LeftMouse> <LeftMouse>:call NextCornerStyle()<CR>
nnoremap <buffer> <silent> <S-3-LeftMouse> <LeftMouse>:call NextCornerStyle()<CR>
nnoremap <buffer> <silent> <S-4-LeftMouse> <LeftMouse>:call NextCornerStyle()<CR>
nnoremap <buffer> <leader>f :call <SID>Fill()<CR>
endfun
" Replace tabs in a:str with the correct number of spaces.
" Need to supply the tabstop value and the offset from the left edge of the
" screen.
" This implements the fix for version 0.3.2.
fun! s:EscapeAll(str)
return escape(a:str, '\.*^$[]')
endfun
let s:spc = ' '
fun! s:Detab(str, ts, offset)
let i = 0
let n = 0
let str = a:str
while 1
let i = match(str, '\t\|\n', i)
if i == -1 | break | endif
if str[i] == "\n"
let n = i + 1
else
let numspaces = a:ts - ((i - n + a:offset) % a:ts)
let str = substitute(str, '\t', strpart(s:spc, 0, numspaces ), '')
endif
let i = i + 1
endwhile
return str
endfun
fun! PasteTransparent() range
normal! gv"oy
let above = b:above
"
"s:Detab() has a noticeable impact on speed. So make sure there
"really are tabs before removing them!
if @o =~ '\t'
let below = s:Detab(@o,&ts,virtcol('.')-1)
else
let below = @o
endif
"Need this if..endif, because you can't write .\{0} in a regexp.
if above[0] == ' '
let above = substitute(above, ' ', escape(below[0],'&'), '')
endif
let i = 1
while i < strlen(above)
if above[i] == ' '
let above = substitute(above, '\(.\{'.i.'}\).', '\1'.escape(below[i],'&'), '')
endif
let i = i + 1
endwhile
let @p = above
"normal! ms
let pos = SavePos(0)
$ put p
"Need the zero. If @p has leading spaces, cursor will be on first non-blank
"of the line.
normal! 0G$h"pydG
exe pos
endfun
fun! Move()
if exists("b:vis_just_saved")
"TRAILING SPACE
normal! gvr
let b:vis_just_saved = 0
endif
endfun
fun! VisSaveDims() range
normal! gv
if virtcol(".") > b:Vis_start_col
let b:viswidth = virtcol(".") - b:Vis_start_col + 1
else
let b:viswidth = b:Vis_start_col - virtcol(".") + 1
endif
if line(".") > b:Vis_start_line
let b:visheight = line(".") - b:Vis_start_line + 1
else
let b:visheight = b:Vis_start_line - line(".") + 1
endif
let b:vis_just_saved = 1
let b:erase_or_drag = 'drag'
endfun
fun! VisWithSavedDims()
if b:viswidth == 1 && b:visheight == 1
return
elseif b:viswidth == 1
exe 'normal! '.(b:visheight-1).'jo'
elseif b:visheight == 1
exe 'normal! '.(b:viswidth-1).'lo'
else
exe 'normal! '.(b:viswidth-1).'l'.(b:visheight-1).'jo'
endif
endfun
fun! NoSketch()
let &go = b:Sketch_savedguiopts
let &mousemodel = b:Sketch_savedmousemodel
setl ve=
nunmap <buffer> <LeftMouse>
nunmap <buffer> <LeftDrag>
"Arrow
nunmap <buffer> <2-LeftMouse>
"Single vert bar
nunmap <buffer> <3-LeftMouse>
"Painting
nunmap <buffer> <C-LeftMouse>
nunmap <buffer> <C-LeftDrag>
nunmap <buffer> <M-LeftMouse>
nunmap <buffer> <M-2-LeftMouse>
nunmap <buffer> <M-3-LeftMouse>
nunmap <buffer> <M-4-LeftMouse>
"Selection
nunmap <buffer> <RightMouse>
vunmap <buffer> <RightDrag>
vunmap <buffer> <RightRelease>
vunmap <buffer> <S-RightMouse>
nunmap <buffer> <S-RightDrag>
nunmap <buffer> <RightDrag>
"Erase
nunmap <buffer> <C-RightMouse>
nunmap <buffer> <C-RightDrag>
nunmap <buffer> <M-RightMouse>
nunmap <buffer> <M-RightDrag>
vunmap <buffer> <RightMouse>
vunmap <buffer> <2-RightMouse>
"Fill
vunmap <buffer> <LeftMouse>
vunmap <buffer> <S-LeftMouse>
vunmap <buffer> <C-LeftMouse>
nunmap <buffer> <S-LeftMouse>
nunmap <buffer> <S-2-LeftMouse>
nunmap <buffer> <S-3-LeftMouse>
nunmap <buffer> <S-4-LeftMouse>
nunmap <buffer> <leader>f
endfun
fun! SketchFillBrush(style) range
if MarkVisEnd() == 'abort'
return
endif
if a:style == 'opaque'
exe "normal! gvr".b:brushes[0]
endif
endfun
fun! SketchFillBox(style) range
"Fixes bug 3.
if MarkVisEnd() == 'abort'
return
endif
if a:style == 'opaque'
"TRAILING SPACE
normal! gvr
endif
if b:Vis_start_line < b:Vis_end_line
let top = b:Vis_start_line
let bot = b:Vis_end_line
else
let top = b:Vis_end_line
let bot = b:Vis_start_line
endif
if b:Vis_start_col < b:Vis_end_col
let left = b:Vis_start_col
let right = b:Vis_end_col
else
let left = b:Vis_end_col
let right = b:Vis_start_col
endif
"Lots of bugginess if we are in virtual columns, so lets pad it first.
let lnum = top
while lnum <= bot
let line = getline(lnum)
"Handle lines with tabs
"X:let len = strlen(line)
exe "norm! " . lnum . "G"
let len = virtcol("$") - 1
if (len) < right "pad the line.
let i = 1
while i < (right - len)
let line = line . ' '
let i = i + 1
endwhile
call setline(lnum, line)
endif
let lnum = lnum + 1
endwhile
"Don't draw corners. If we are just drawing a line then we want to check
"for a join first. Need to test for greater than 1, not 0 because we are
"not including corners.
if (bot-top) > 1
"Wake up vim. Also fixes bug 2, bug 6.
normal! lh
call Cursor(top,left)
let n = (bot-top-2)==0 ? '' : (bot-top-2).'j'
normal! lh
exe 'normal! jlh'.n.'r|'
call Cursor(top,right)
normal! lh
"Seems to be a bug. If I don't do lh after visual block selction, it goes
"wrong.
exe 'normal! jlh'.n.'r|'
endif
if (right-left) > 1
"Also bugginess if we don't wake vim up with a cursor movement before
"calling cursor()
normal! l
call Cursor(top,left)
let n = (right-left-2)==0 ? '' : (right-left-2).'l'
exe 'normal! llh'.n.'r-'
normal! l
call Cursor(bot,left)
exe 'normal! llh'.n.'r-'
endif
"Want to draw the '-' last, because we have triple click to insert a single
"'|'. So now we can rightclick leftclick to insert a single '-' over a
"character.
if top == bot
"Horizontal line.
call Cursor(top,left)
normal! lh
if GetChar() == '|'
normal! r+
else
normal! r-
endif
call Cursor(top,right)
normal! lh
if GetChar() == '|'
normal! r+
else
normal! r-
endif
elseif left == right
"Vertical line.
call Cursor(top,left)
normal! lh
if GetChar() == '-'
normal! r+
else
normal! r|
endif
call Cursor(bot,left)
if GetChar() == '-'
normal! r+
else
normal! r|
endif
else
if b:roundcorners == 1
call Cursor(top,left)
normal! r.
call Cursor(top,right)
normal! r.
call Cursor(bot,left)
normal! r'
call Cursor(bot,right)
normal! r'
else
call Cursor(top,left)
normal! r+
call Cursor(top,right)
normal! r+
call Cursor(bot,left)
normal! r+
call Cursor(bot,right)
normal! r+
endif
endif
call Cursor(b:Vis_end_line, b:Vis_end_col)
" Determine shape of box to set the direction.
"/2 because letters are narrower than they are tall.
if (right-left)/2 >= (bot-top)
"Short fat box.
if b:Vis_end_col > b:Vis_start_col
call SketchSavePos('r')
else
call SketchSavePos('l')
endif
else
"Tall thin box.
if b:Vis_end_line > b:Vis_start_line
call SketchSavePos('d')
else
call SketchSavePos('u')
endif
endif
echo '['.b:brushes[0].']['.((b:roundcorners==1)?'Round':'Square').']'
endfun
fun! MoreLines(where)
if a:where == 'below'
let pos = SavePos(0)
normal! G12o
exe pos
else
let pos = SavePos(12)
normal! 1G12O
exe pos
endif
endfun
fun! SavePos(offset)
let c = virtcol(".")
let l1 = line(".")
normal! H
let l2 = line(".")
return 'call Cursor('.(l2 + a:offset).',1)| exe "normal! zt"|call Cursor('.(l1 + a:offset).',1)|exe "normal! '.(c-1).'l"'
endfun
fun! SketchPaint()
call SketchSavePos('auto')
if b:brushes[0] == '.'
normal! r.
elseif b:brushes[0] == ':'
normal! r:lr:h
elseif b:brushes[0] == '#'
normal! hr#lr#lr#hhjr#lr#lr#hk
endif
silent call AddLineIfAtBoundary()
endfun
fun! SketchErase(size) range
if winnr() != g:Sketch_winnr
return
endif
if a:size == 'vblock'
if MarkVisEnd() == 'abort'
return
endif
"TRAILING SPACE
normal! gvr
else
if a:size == ''
let size = b:Sketch_erasesize
else
let size = a:size
endif
if size == 'small'
"TRAILING SPACE
normal! r
let b:Sketch_erasesize = size
elseif size == 'big'
normal! hkR hhjR hhjR hk
let b:Sketch_erasesize = size
endif
call SketchSavePos('auto')
silent call AddLineIfAtBoundary()
endif
echo '['.b:brushes[0].']['.((b:roundcorners==1)?'Round':'Square').']'
endfun
fun! SketchClick(char)
if a:char == '>'
call SketchArrow()
elseif a:char == '|'
call SketchBar()
else
"This fixes bug 7.
if IsBlankChar() || (GetChar() != '-' && GetChar() != '|')
exe "normal! r" . a:char
let b:Sketch_firstclick = 1
else
normal! r+
let b:Sketch_firstclick = 0
endif
if a:char == '-'
call SketchSavePos('r')
else
call SketchSavePos('d')
endif
endif
silent call AddLineIfAtBoundary()
endfun
fun! AddLineIfAtBoundary()
"Tried adding line at top of file as well, but I didn't think it
"worked very well.
if line(".") == line("$")
$ put _
endif
call Cursor(b:Sketch_line,b:Sketch_col)
endfun
fun! SketchArrow()
if b:Sketch_dir2 == 'r'
normal! r>
elseif b:Sketch_dir2 == 'l'
normal! r<
elseif b:Sketch_dir2 == 'u'
normal! r^
elseif b:Sketch_dir2 == 'd'
normal! rv
endif
call SketchSavePos(b:Sketch_dir2)
echo '['.b:brushes[0].']['.((b:roundcorners==1)?'Round':'Square').']'
endfun
fun! SketchBar()
normal! r|
call SketchSavePos(b:Sketch_dir)
endfun
fun! Debug()
echo "Line: ".b:Sketch_line." Col: ".b:Sketch_col." Dir: ".b:Sketch_dir
endfun
fun! SketchDrag()
"Moving vertically (current col == last saved col)
if virtcol(".") == b:Sketch_col
"This fixes bug 7.
if IsBlankChar() || (GetChar() !~ '-\|+')
normal! r|
else
normal! r+
endif
if b:Sketch_dir =~ 'r\|l'
if b:Sketch_firstclick
call SketchAtSavedPos('|')
else
if b:roundcorners == 1
"Previously moved horizontally, now moving up
if line(".") < b:Sketch_line
call SketchAtSavedPos("'")
"Moving down
else
call SketchAtSavedPos('.')
endif
else
call SketchAtSavedPos('+')
endif
endif
endif
"[TODO] I think this is redundant, as rightrelease now sets this.
if line(".") > b:Sketch_line
call SketchSavePos('d')
else
call SketchSavePos('u')
endif
"Otherwise, must be moving horizontally
else
"This fixes bug 7.
if IsBlankChar() || (GetChar() !~ '|\|+')
normal! r-
else
normal! r+
endif
if b:Sketch_dir =~ 'd\|u'
if b:roundcorners == 1
"Was moving up
if b:Sketch_dir == 'u'
call SketchAtSavedPos(".")
"Moving down
else
call SketchAtSavedPos("'")
endif
else
call SketchAtSavedPos('+')
endif
endif
"I think this is redundant, as rightrelease now sets this.
if virtcol(".") > b:Sketch_col
call SketchSavePos('r')
else
call SketchSavePos('l')
endif
endif
let b:Sketch_firstclick = 0
silent call AddLineIfAtBoundary()
echo '['.b:brushes[0].']['.((b:roundcorners==1)?'Round':'Square').']'
endfun
fun! IsBlankChar()
let char = GetChar()
if (char == ' ') || (char == '')
return 1
else
return 0
endif
endfun
fun! GetChar()
return strpart(getline("."),virtcol(".")-1,1)
endfun
fun! SketchSavePos(dir)
let b:Sketch_dir2 = b:Sketch_dir
if a:dir == 'auto'
if (line(".") > b:Sketch_line)
let b:Sketch_dir = 'd'
elseif (line(".") < b:Sketch_line)
let b:Sketch_dir = 'u'
elseif (virtcol(".") > b:Sketch_col)
let b:Sketch_dir = 'r'
elseif (virtcol(".") < b:Sketch_col)
let b:Sketch_dir = 'l'
endif
else
let b:Sketch_dir = a:dir
endif
let b:Sketch_line = line(".")
let b:Sketch_col = virtcol(".")
endfun
fun! SketchAtSavedPos(char)
let col = virtcol(".")
let line = line(".")
call Cursor(b:Sketch_line,b:Sketch_col)
exe "normal! r".a:char
"You can have fun if you reverse line and col
call Cursor(line,col)
endfun
"
"-- Flood fill functions -----------------------------------
"
fun! s:Field(string, num, delim)
" Return the field specified by integer a:num from a:string where fields are
" delimited by the character a:delim.
if a:num < 1
return ''
elseif a:num == 1
return matchstr(a:string, '[^' . a:delim . ']*')
else
return matchstr(a:string, '\([^' . a:delim . ']*' . a:delim . '\)\{' . (a:num - 1) . '}\zs[^' . a:delim . ']*')
endif
endfun
fun! s:Unlet()
let i = 1
while i <= s:TOS
exe 'unlet s:fillinfo' . i
let i = i + 1
endwhile
endfun
" The fill algorithm might as well be recursive. However Vim has reasonably
" strict limits on the depth of recursion. It is quite hard to break this
" algorithm if done recursively, but it is possible with a large complicated
" diagram. Just in case this is a problem for someone, I've implemented the
" algorithm imperatively, using a couple of explicit stacks. Happily, this
" also seems to speed things up.
"
" The fill starts off horizontally. When a line has been filled as far as it
" can, the line, the leftmost cell and the rightmost cell are pushed onto a
" stack. Information from a stack is popped, and this is used to check each
" cell above and below those just filled. If any of these are not a boundary
" character then go back to the start of this paragraph.
"
" If I also encode the vertical direction in which the fill is progressing,
" then I can make a simple optimization to reduce the number of cells that
" need checking.
fun! s:PushFillInfo(line, L, R, dir) "dir can be 'u' up or 'd' down
let s:TOS = s:TOS + 1
exe 'let s:fillinfo' . s:TOS . '="' . a:line . ',' . a:L . ',' . a:R . ',' . a:dir . '"'
endfun
fun! s:PopFillInfo()
exe 'let fillinfo = s:fillinfo' . s:TOS
exe 'unlet s:fillinfo' . s:TOS
let s:TOS = s:TOS - 1
let line = s:Field(fillinfo, 1, ',')
let L = s:Field(fillinfo, 2, ',')
let R = s:Field(fillinfo, 3, ',')
let dir = s:Field(fillinfo, 4, ',')
return 'let line=' . line . '|let L=' . L . '|let R=' . R . '|let dir="' . dir . '"'
endfun
fun! s:IsBoundary()
return @" ==# s:fillchar || @" == '|' || @" == '-' || @" == '' || @" == '+' || @" == '\' || @" == '/' || @" == '<' || @" == '>'
endfun
" Returns a string that you can execute to store the leftmost column in L and
" the rightmost column in R. E.g. :exe Fill_line()
fun! s:Fill_line()
let line = line('.') | let col = col('.')
"Fill current char
let R = col | let L = col
silent norm! yl
if !s:IsBoundary()
exe 'silent norm! r' . s:fillchar
endif
"Fill to the left
let i = col
let L = 1
while i > 1
silent norm! hyl
let i = i - 1
if s:IsBoundary()
let L = i + 1
break
else
exe 'silent norm! r' . s:fillchar
endif
endwhile
call Cursor(line, col)
"Fill to the right
let i = col
let R = col('$') - 1
while i < (col('$') - 1)
silent norm! lyl
let i = i + 1
if s:IsBoundary()
let R = i - 1
break
else
exe 'silent norm! r' . s:fillchar
endif
endwhile
return 'let L=' . L . '|let R=' . R
endfun
fun! <SID>Fill()
" The variables used for the stack get unlet as the function progresses.
" However if the user hits <C-C> before filling has finished, we could
" potentially be left with many variables in memory. So each time Fill() is
" run, first check that nothing is left over from the previous Fill(). If
" it is then remove the variables before prompting the user for the fill
" character. This way the user won't notice the delay.
if exists("s:fillinfo1")
call s:Unlet()
endif
let s:TOS = 0
"Get input from user. Use only the first character supplied.
call inputsave()
let s:fillchar = input("Enter the fill character: ")[0]
call inputrestore()
if s:fillchar == ''
redraw!
echo 'Fill aborted.'
return
endif
echo 'Working, please wait.'
let saved_ve = &ve
let &ve = ''
let lastline = line('$')
silent norm! yl
if s:IsBoundary()
redraw!
echo 'On a boundary.'
return
endif
let line = line('.') | let col = col('.')
let restorecursor = "call Cursor(" . line . "," . col . ")"
silent exe s:Fill_line()
call s:PushFillInfo(line, L, R, 'u')
call s:PushFillInfo(line, L, R, 'd')
while s:TOS > 0
exe s:PopFillInfo()
let l = L | let r = R
if dir == 'u'
if line > 1
exe 'silent norm! ' . (line - 1). 'G' . L . '|'
else
continue
endif
let i = l
while i <= r
silent norm! yl
if !s:IsBoundary()
silent exe s:Fill_line()
call s:PushFillInfo(line - 1, L, R, 'u')
" Eliminate some (but not all) of the cells already checked.
" This is actually slightly faster than eliminating all the
" cells already checked! (shown underneath). Perhaps because of
" the extra stack accesses being more expensive than looping
" over a few already checked cells?
if R > r || L < l
call s:PushFillInfo(line - 1, L, R, 'd')
endif
"if L < l
" call s:PushFillInfo(line - 1, L, l - 1, 'd')
"endif
"if R > r
" call s:PushFillInfo(line - 1, l + 1, R, 'd')
"endif
" Finish early if possible.
if R >= r
break
else
let i = R + 1
endif
else
let i = i + 1
endif
exe 'silent norm! ' . i . '|'
endwhile
else "assume dir == 'd'
if line < lastline
exe 'silent norm! ' . (line + 1). 'G' . L . '|'
else
continue
endif
let i = l
while i <= r
silent norm! yl
if !s:IsBoundary()
silent exe s:Fill_line()
call s:PushFillInfo(line + 1, L, R, 'd')
" Eliminate some (but not all) of the cells already checked.
if R > r || L < l
call s:PushFillInfo(line + 1, L, R, 'u')
endif
"if L < l
" call s:PushFillInfo(line + 1, L, l - 1, 'u')
"endif
"if R > r
" call s:PushFillInfo(line + 1, l + 1, R, 'u')
"endif
" Finish early if possible.
if R >= r
break
else
let i = R + 1
endif
else
let i = i + 1
endif
exe 'silent norm! ' . i . '|'
endwhile
endif
endwhile
redraw!
let &ve = saved_ve
exe restorecursor
echo 'Fill finished.'
endfun
"
"Implement diagonals like this:
" /
" \/
" /\ _
" / \ /|
" _ _ .----------------' \ /
" /| |\ \ / / X
" / \ \| |/ / / \
" <20> <20> / \
"
"
"Implement painting with colours like this:
"syn match y /y/
"syn match x /x/
"hi y guifg=#cc7788 guibg=#cc7788
"hi x guifg=#8877cc guibg=#8877cc
"map \x :let brush = 'x'<CR>
"map \y :let brush = 'y'<CR>
"map <rightdrag> <leftmouse>:exe 'normal! r'.brush <CR>
"map <rightmouse> <leftmouse>:exe 'normal! r'.brush <CR>
"
" Even though Sketch doesn't have diagonals yet,
" this was easy thanks to the copy+drag actions.
" +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
" |\...........\...........\...........\...........\...........\...........\...........\...........\
" |:\...........\...........\...........\...........\...........\...........\...........\...........\
" |::+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
" |::|\...........\...........\ |::| |::| |::| |::|\...........\...........\
" |::|:\...........\...........\ |::| |::| |::| |::|:\...........\...........\
" +::|::+-----------+-----------+-----+::|--------+::|--------+::|--------+::|::+-----------+-----------+
" |\:|::|\...........\...........\.....\:|.........\:|.........\:|.........\:|::|\...........\...........\
" |:\|::|:\...........\...........\.....\|..........\|..........\|..........\|::|:\...........\...........\
" |::+::|::+-----------+-----------+-----+-----------+-----------+-----------+::|::+-----------+-----------+
" |::|\:|::|\...........\...........\ |::| |::| |::| |::|\:|::|\...........\...........\
" |::|:\|::|:\...........\...........\|::| |::| |::| |::|:\|::|:\...........\...........\
" +::|::+::|::+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
" \:|::|\:|::|\...........\...........\...........\...........\...........\...........\...........\...........\
" \|::|:\|::|:\...........\...........\...........\...........\...........\...........\...........\...........\
" +::|::|::|::+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
" \:|::|\:|::|......\:|::|......\:|::| |::| |::| |::|\:|::|\:|::|......\:|::|......\:|::|
" \|::|:\|::|.......\|::|.......\|::| |::| |::| |::| \|::|:\|::|.......\|::|.......\|::|
" +::|::+::|--------+::|--------+::|--------+::|--------+::|--------+::|--------+::|--------+::|--------+::|
" \:|::|\:|.........\:|.........\.|.........\:|.........\:|.........\:|.........\:|.........\:|.........\:|
" \|::|:\|..........\|..........\|..........\|..........\|..........\|..........\|..........\|..........\|
" +::|::+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
" \:|::|......\:|::|......\:|::| |::| |::| |::| \:|::|......\:|::|......\:|::|
" \|::|.......\|::|.......\|::| |::| |::| |::| \|::|.......\|::|.......\|::|
" +::|--------+::|--------+::|--------+::|--------+::|--------+::|--------+::|--------+::|--------+::|
" \:|.........\:|.........\:|.........\:|.........\:|.........\:|.........\:|.........\:|.........\:|
" \|..........\|..........\|..........\|..........\|..........\|..........\|..........\|..........\|
" +-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+
"
"
" +--------------------+ +--------------------+ +--------------------+ +--------------------+
" | | | | | | | |
" +---|----------------+ | +---|----------------+ | +---|----------------+ | +---|----------------+ |
" | | | | | | | | | | | | | | | |
" | | +--------------------+ | +--------------------| | +--------------------+ | +--------------------+
" | | | | | | | | | | | | | | | | | | | | |
" | +---|----------------+ | +---|----------------+ | +---|----------------+ | +---|----------------+ |
" | | | | | | | | | | | | | | | | | | | | |
" | | | | | | | | | | | | | | | | | | | | |
" | |---|----------------| | |---|----------------| | |---|----------------| | |---|----------------| |
" | | | | | | | | | | | | | | | | | | | | |
" +---|---|------------+ | |---|---|------------+ | +---|---|------------+ | |---|---|------------+ | |
" |---|----------------| | |---|----------------| | |---|----------------| | |---|----------------| |
" | +--------------------+ | +--------------------+ | +--------------------+ | +--------------------+
" +---|----------------+ | +---|----------------+ | +---|----------------+ | +---|----------------+ |
" | +--------------------+ | +--------------------+ | +--------------------+ | +--------------------+
" | | +--------------------+ | +--------------------| | +--------------------+ | +--------------------+
" | | | | | | | | | | | | | | | | | | | | |
" | +---|----------------+ | +---|----------------+ | +---|----------------+ | +---|----------------+ |
" | | | | | | | | | | | | | | | | | | | | |
" | | | | | | | | | | | | | | | | | | | | |
" | |---|----------------| | |---|----------------| | |---|----------------| | |---|----------------| |
" | | | | | | | | | | | | | | | | | | | | |
" +---|---|------------+ | |---|---|------------+ | +---|---|------------+ | |---|---|------------+ | |
" | | | | | | | | | | | | | | | |
" | +--------------------+ | +--------------------+ | +--------------------+ | +--------------------+
" | | | | | | | |
" +--------------------+ +--------------------+ +--------------------+ +--------------------+
"
"
"
" Not that you'd ever want to draw anything like this, but it is easy. Big
" screen helps :-)