1064 lines
37 KiB
VimL
1064 lines
37 KiB
VimL
" 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 :-)
|