diff --git a/Notes.pdf b/Notes.pdf new file mode 100644 index 0000000..92dcf79 Binary files /dev/null and b/Notes.pdf differ diff --git a/additions_for_your_.vimrc b/additions_for_your_.vimrc new file mode 100644 index 0000000..7a17147 --- /dev/null +++ b/additions_for_your_.vimrc @@ -0,0 +1,158 @@ +"====[ Make the 81st column stand out ]==================== + + " EITHER the entire 81st column, full-screen... + highlight ColorColumn ctermbg=magenta + set colorcolumn=81 + + " OR ELSE just the 81st column of wide lines... + highlight ColorColumn ctermbg=magenta + call matchadd('ColorColumn', '\%81v', 100) + + " OR ELSE on April Fools day... + highlight ColorColumn ctermbg=red ctermfg=blue + exec 'set colorcolumn=' . join(range(2,80,3), ',') + + +"=====[ Highlight matches when jumping to next ]============= + + " This rewires n and N to do the highlighing... + nnoremap n n:call HLNext(0.4) + nnoremap N N:call HLNext(0.4) + + + " EITHER blink the line containing the match... + function! HLNext (blinktime) + set invcursorline + redraw + exec 'sleep ' . float2nr(a:blinktime * 1000) . 'm' + set invcursorline + redraw + endfunction + + " OR ELSE ring the match in red... + function! HLNext (blinktime) + highlight RedOnRed ctermfg=red ctermbg=red + let [bufnum, lnum, col, off] = getpos('.') + let matchlen = strlen(matchstr(strpart(getline('.'),col-1),@/)) + echo matchlen + let ring_pat = (lnum > 1 ? '\%'.(lnum-1).'l\%>'.max([col-4,1]) .'v\%<'.(col+matchlen+3).'v.\|' : '') + \ . '\%'.lnum.'l\%>'.max([col-4,1]) .'v\%<'.col.'v.' + \ . '\|' + \ . '\%'.lnum.'l\%>'.max([col+matchlen-1,1]) .'v\%<'.(col+matchlen+3).'v.' + \ . '\|' + \ . '\%'.(lnum+1).'l\%>'.max([col-4,1]) .'v\%<'.(col+matchlen+3).'v.' + let ring = matchadd('RedOnRed', ring_pat, 101) + redraw + exec 'sleep ' . float2nr(a:blinktime * 1000) . 'm' + call matchdelete(ring) + redraw + endfunction + + " OR ELSE briefly hide everything except the match... + function! HLNext (blinktime) + highlight BlackOnBlack ctermfg=black ctermbg=black + let [bufnum, lnum, col, off] = getpos('.') + let matchlen = strlen(matchstr(strpart(getline('.'),col-1),@/)) + let hide_pat = '\%<'.lnum.'l.' + \ . '\|' + \ . '\%'.lnum.'l\%<'.col.'v.' + \ . '\|' + \ . '\%'.lnum.'l\%>'.(col+matchlen-1).'v.' + \ . '\|' + \ . '\%>'.lnum.'l.' + let ring = matchadd('BlackOnBlack', hide_pat, 101) + redraw + exec 'sleep ' . float2nr(a:blinktime * 1000) . 'm' + call matchdelete(ring) + redraw + endfunction + + " OR ELSE just highlight the match in red... + function! HLNext (blinktime) + let [bufnum, lnum, col, off] = getpos('.') + let matchlen = strlen(matchstr(strpart(getline('.'),col-1),@/)) + let target_pat = '\c\%#'.@/ + let ring = matchadd('WhiteOnRed', target_pat, 101) + redraw + exec 'sleep ' . float2nr(a:blinktime * 1000) . 'm' + call matchdelete(ring) + redraw + endfunction + + +"====[ Make tabs, trailing whitespace, and non-breaking spaces visible ]====== + + exec "set listchars=tab:\uBB\uBB,trail:\uB7,nbsp:~" + set list + + +"====[ Swap : and ; to make colon commands easier to type ]====== + + nnoremap ; : + nnoremap : ; + + +"====[ Swap v and CTRL-V, because Block mode is more useful that Visual mode "]====== + + nnoremap v + nnoremap v + + vnoremap v + vnoremap v + + +"====[ Always turn on syntax highlighting for diffs ]========================= + + " EITHER select by the file-suffix directly... + augroup PatchDiffHighlight + autocmd! + autocmd BufEnter *.patch,*.rej,*.diff syntax enable + augroup END + + " OR ELSE use the filetype mechanism to select automatically... + filetype on + augroup PatchDiffHighlight + autocmd! + autocmd FileType diff syntax enable + augroup END + + +"====[ Open any file with a pre-existing swapfile in readonly mode "]========= + + augroup NoSimultaneousEdits + autocmd! + autocmd SwapExists * let v:swapchoice = 'o' + autocmd SwapExists * echomsg ErrorMsg + autocmd SwapExists * echo 'Duplicate edit session (readonly)' + autocmd SwapExists * echohl None + autocmd SwapExists * sleep 2 + augroup END + + " Also consider the autoswap_mac.vim plugin (but beware its limitations) + + +"====[ Mappings to activate spell-checking alternatives ]================ + + nmap ;s :set invspell spelllang=en + nmap ;ss :set spell spelllang=en-basic + + " To create the en-basic (or any other new) spelling list: + " + " :mkspell ~/.vim/spell/en-basic basic_english_words.txt + " + " See :help mkspell + + +"====[ Make CTRL-K list diagraphs before each digraph entry ]=============== + + inoremap ShowDigraphs() + + function! ShowDigraphs () + digraphs + call getchar() + return "\" + endfunction + + " But also consider the hudigraphs.vim and betterdigraphs.vim plugins, + " which offer smarter and less intrusive alternatives + diff --git a/intro.swtc b/intro.swtc new file mode 100644 index 0000000..9ca290d --- /dev/null +++ b/intro.swtc @@ -0,0 +1,74 @@ +A long time ago in a terminal far, +far away.... + + + [ __ __ _____ _____ _____ ] + [ |* \/ *| /**_**\|**__*\|*____| ] + [ |*|\/|*||**/ \**|*|__|*|*__| ] + [ |*| |*|\**\_/**|**_**/|*|___ ] + [ |_| |_________/|_| \_\|_____| ] + [ __ __ _____ __ __ ] + [ \*\ /*/|_***_||* \/ *| ] + [ \*\/*/ |*| |*|\/|*| ] + [ \**/ _|*|__|*| |*| ] + [ \/ |________| |_| ] + + + [[ __ __ __ ____ ____ ]] + [[ |* \/ *| /**\|**_*\|*___| ]] + [[ |*|\/|*||*[]*|*|_|*|*_|_ ]] + [[ |_| |______/|_|\_\|____| ]] + [[ __ __ _____ __ __ ]] + [[ \*\/*/|_***_|* \/ *| ]] + [[ \**/ _|*|_|*|\/|*| ]] + [[ \/ |_______| |_| ]] + + + + [[[ _ _ __ ___ ___ ]]] + [[[ ||\/||/**\|*_*>|*__| ]]] + [[[ || |____/|_|\\|___| ]]] + [[[ _ _ ___ _ _ ]]] + [[[ \\//|_*_| \/ | ]]] + [[[ \/ |____|\/|| ]]] + + + + [[[[ |\/|/\|<>>|*_| ]]]] + [[[[ ||||\/||\\|__| ]]]] + [[[[ \\//|||\/| ]]]] + [[[[ \/ |||||| ]]]] + + + +> Episode IV < +> < +> A NEW HOPE < +> < +> < +|It is a period of software war.| +|Agile dev teams, checking out from| +|a distributed codebase, have won| +|their first symbolic victories| +|against the fanatical GNU Empire.| +| | +|During the battle, Open Source| +|hackers have managed to clone an| +|unauthorized source archive of the| +|Empire's new ultimate weapon, the| +|EMACS TAR: a fully operational| +|operating system with sufficient| +|Alt-Meta-Ctrl-commands to destroy| +|the carpal tunnels of an entire| +|planet. | +| | +|Evading GNU's copy-sinister agenda| +|the mysterious Vimlord, Con Wei,| +|dashes off new Vimscript tools in| +|his heavily fortified text-editor,| +|custodian of the latest tips and| +|plugins that may save his people| +|effort, and restore efficiency to| +|the keyboard.... | + + diff --git a/plugin/SWTC.vim b/plugin/SWTC.vim new file mode 100644 index 0000000..05d7b4b --- /dev/null +++ b/plugin/SWTC.vim @@ -0,0 +1,339 @@ +" Vim global plugin for Star Wars crawls +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"###################################################################### +"## ## +"## To use: ## +"## ## +"## :SWTC ## +"## ## +"## See file 'intro.swtc' for the crawl-specification syntax ## +"## ## +"###################################################################### + + +" If already loaded, we're done... +if exists("loaded_SWcrawl") + finish +endif +let loaded_SWcrawl = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + +" Set up the actual colon command... +command! -nargs=1 -complete=file SWTC call SWcrawl() + + +" Implementation.... + +let s:CRAWL_SPEED = 1 "(lines per second) +let s:STAR_DENSITY = 50 "(pixels per star, i.e. 1 star per STAR_DENSITY pixels) +let s:STARFIELD_HEIGHT = 2 "(screens deep) + +let s:LOGO_LINE1 = '^\s*\[\zs.*\ze\]\s*$' +let s:LOGO_LINE2 = '^\s*\[\[\zs.*\ze\]\]\s*$' +let s:LOGO_LINE3 = '^\s*\[\[\[\zs.*\ze\]\]\]\s*$' +let s:LOGO_LINE4 = '^\s*\[\[\[\[\zs.*\ze\]\]\]\]\s*$' +let s:CENTRED_CRAWL_LINE = '^\s*[>]\s*\zs.\{-}\ze\s*[<]\s*$' +let s:CRAWL_LINE = '^\s*[|]\s*\zs.\{-}\ze\s*[|]\s*$' +let s:PREFACE_LINE = '^\s*\zs.\{-}\ze\s*$' + +highlight SWC_PREFACE ctermfg=cyan +highlight SWC_FADE_LIGHT ctermfg=cyan +highlight SWC_FADE_DARK ctermfg=blue +highlight SWC_LOGO ctermfg=yellow cterm=bold +highlight SWC_CRAWL ctermfg=yellow +highlight SWC_STAR ctermfg=white +highlight SWC_BLACK ctermfg=black ctermbg=black + +let s:PREFACE_POS = { 'x': 10, 'y': 5 } + +function! SWcrawl (textsource) + " Load preface, logo, and text to be crawled... + let preface = [] + let logo1 = [] + let logo2 = [] + let logo3 = [] + let logo4 = [] + let crawl = [] + let centred = [] + let max_crawl_width = 0 + for nextline in readfile(a:textsource) + " Ignore blank lines... + if nextline =~ '^\s*$' + continue + + " Lines in [...] are logo components... + elseif nextline =~ s:LOGO_LINE4 + let logo4 += [ matchstr(nextline, s:LOGO_LINE4) ] + elseif nextline =~ s:LOGO_LINE3 + let logo3 += [ matchstr(nextline, s:LOGO_LINE3) ] + elseif nextline =~ s:LOGO_LINE2 + let logo2 += [ matchstr(nextline, s:LOGO_LINE2) ] + elseif nextline =~ s:LOGO_LINE1 + let logo1 += [ matchstr(nextline, s:LOGO_LINE1) ] + + " Lines in |...| are crawl components... + elseif nextline =~ s:CRAWL_LINE + let next_crawl = matchstr(nextline, s:CRAWL_LINE) + if strlen(next_crawl) > max_crawl_width + let max_crawl_width = strlen(substitute(next_crawl,'\s\+',' ','g')) + endif + let crawl += [ next_crawl ] + let centred += [ 0 ] + + " Lines in >...< are centred crawl components... + elseif nextline =~ s:CENTRED_CRAWL_LINE + let next_crawl = matchstr(nextline, s:CENTRED_CRAWL_LINE) + if strlen(next_crawl) > max_crawl_width + let max_crawl_width = strlen(substitute(next_crawl,'\s\+',' ','g')) + endif + let crawl += [ next_crawl ] + let centred += [ 1 ] + + " Anything else is preface... + else + let preface += [ substitute(matchstr(nextline, s:PREFACE_LINE), "^\s*", repeat(" ",s:PREFACE_POS.x), '') ] + + endif + endfor + + " Ensure all logos available... + let logo1 = len(logo1) ? logo1 : ["YOUR", "LOGO", "HERE"] + let logo2 = len(logo2) ? logo2 : copy(logo1) + let logo3 = len(logo3) ? logo3 : copy(logo2) + let logo4 = len(logo4) ? logo4 : copy(logo3) + + " Save current buffer for final transition effect... + let original_buffer = getline(1,'$') + + " Switch to a new buffer... + let prev_matches = getmatches() + enew! + let b:WIN = { 'x' : winwidth(0), 'y' : winheight(0) } + call setline(1, repeat([""], b:WIN.y + 1)) + + " And hide annoyances... + set lcs= + let old_rulerformat = &rulerformat + let &rulerformat="%#SWC_BLACK#%l" + echo "" + + " Generate starfield... + let stars = SWC_gen_stars() + + " Clear screen... + call setline(1, repeat([""], s:STARFIELD_HEIGHT * b:WIN.y) + original_buffer) + redraw + sleep 2 + + " Start with preface... + call matchadd('SWC_PREFACE', '.', 100) + call setline(s:PREFACE_POS.y, preface) + echo "" + redraw + sleep 5 + + " Clean up... + call clearmatches() + call setline(s:PREFACE_POS.y, repeat([""], len(preface))) + echo "" + redraw + sleep 1 + + " Then show logo receding at centre of screen... + call clearmatches() + call matchadd('SWC_BLACK', '*', 102) + call matchadd('SWC_STAR', '\s\zs[.]\ze\s', 101) + call matchadd('SWC_LOGO', '.', 100) + call SWC_draw_logo(logo1) + call SWC_paint_stars(stars) + echo "" + redraw + sleep 3 + + " Push it away... + call setline(1, repeat([""], b:WIN.y)) + call SWC_draw_logo(logo2) + call SWC_paint_stars(stars) + echo "" + redraw + sleep 500m + + call setline(1, repeat([""], b:WIN.y)) + call SWC_draw_logo(logo3) + call SWC_paint_stars(stars) + echo "" + redraw + sleep 500m + + call setline(1, repeat([""], b:WIN.y)) + call SWC_draw_logo(logo4) + call SWC_paint_stars(stars) + echo "" + redraw + sleep 500m + + + " Clean up... + call clearmatches() + call matchadd('SWC_STAR', '\s\zs[.]\ze\s', 101) + call setline(1, repeat([""], b:WIN.y)) + call SWC_paint_stars(stars) + echo "" + redraw + sleep 2 + + " Run crawl... + call clearmatches() + call matchadd('SWC_CRAWL', '.', 100) + call matchadd('SWC_STAR', '\s\zs[.]\ze\s', 101) + for offset_from_bottom in range(1, len(crawl) + b:WIN.y) + let crawl_line = offset_from_bottom < b:WIN.y ? 0 : offset_from_bottom - b:WIN.y + 1 + for screen_line in range(1, b:WIN.y) + if screen_line >= b:WIN.y - offset_from_bottom && crawl_line < len(crawl) + let padded_line = SWC_pad(crawl[crawl_line], screen_line, centred[crawl_line], max_crawl_width) + call setline(screen_line, padded_line) + let crawl_line += 1 + else + call setline(screen_line, "") + endif + endfor + call SWC_paint_stars(stars) + echo "" + redraw + exec 'sleep ' . s:trunc(1000/s:CRAWL_SPEED) . 'm' + if getchar(0) || offset_from_bottom > len(crawl) && padded_line !~ '\S' + break + endif + endfor + + " Pan starfield down... + call matchadd('SWC_FADE_DARK', '[^.]', 200) + sleep 200m + for offset_from_top in range(1, s:STARFIELD_HEIGHT * b:WIN.y) + 1delete + redraw + exec 'sleep ' . (200 - 2 * offset_from_top) . 'm' + endfor + sleep 200m + + + " Switch back to previous buffer and restore normal highlighting... + edit! # + call setmatches(prev_matches) + let &rulerformat = old_rulerformat + redraw + +endfunction + +function s:trunc (n) + return str2nr(string( a:n )) +endfunction + +function! SWC_draw_logo (logo) + let logo = copy(a:logo) + + " Find centre for logo... + let logo_width = 0 + for line in logo + if strlen(line) > logo_width + let logo_width = strlen(line) + endif + endfor + let logo_pos_x = (b:WIN.x - logo_width) / 2 + let logo_pos_y = (b:WIN.y - len(logo)) / 2 + + " Move logo to centre... + call map(logo, "repeat(' ', logo_pos_x) . v:val") + + " Draw logo + call setline(logo_pos_y, logo) + +endfunction + +function! SWC_pad (text, y_pos, centred, max_text_width) + + " Does this need padding??? + let words = split(a:text, '\s\+') + if len(words) < 1 + return a:text + endif + + " How many unpadded characters are there??? + let unpadded_width = 0 + for word in words + let unpadded_width += strlen(word) + endfor + + " How much padding is needed??? + let rel_y = (2.0 * a:y_pos / b:WIN.y) - 1.0 + let stretched_width = s:trunc( a:max_text_width + rel_y * (b:WIN.x - a:max_text_width) ) + let required_padding = max([ 0, stretched_width - unpadded_width ]) + let indent = (b:WIN.x - stretched_width) / 2 + let gap_count = len(words) - 1 + + " Is this a last line??? + let tight = a:centred || strlen(a:text) < 0.9 * a:max_text_width + + " Insert padding... + if a:y_pos >= b:WIN.y/2 + let min_padding_needed_for = gap_count + if tight + let min_pad_per_gap = max([ 1, s:trunc(rel_y * 6.0) ]) + else + let min_pad_per_gap = max([ 1, required_padding / gap_count ]) + let leftover_padding = required_padding - gap_count * min_pad_per_gap + let min_padding_needed_for = min([ gap_count, gap_count - leftover_padding ]) + endif + let padded_text = join(words[0 : min_padding_needed_for], repeat(" ", min_pad_per_gap)) + \ . repeat(" ", min_pad_per_gap+1) + \ . join(words[min_padding_needed_for+1 : -1], repeat(" ", min_pad_per_gap+1)) + let padded_text = substitute(padded_text, '\s*$', '', '') + + " Or remove chars (in the distance)... + elseif a:text =~ '\S' +" let delta = s:trunc( 8.0 * (b:WIN.y/2 - a:y_pos) ) +" let greeked_len = max([ 0, strlen(substitute(a:text, '^\s*\|\s*$', '', 'g')) - delta ]) + let greeked_len = tight ? stretched_width * (unpadded_width + gap_count) / a:max_text_width : stretched_width + let padded_text = repeat('~', greeked_len) + + " Or ignore it... + else + let padded_text = "" + + endif + + " Indent to centre... + let padded_text = substitute(padded_text, '\s*$', '', '') + let max_ever_padding = b:WIN.x - a:max_text_width + let indent = a:centred ? (b:WIN.x - strlen(padded_text))/2 + \ : indent + return repeat(" ", indent) . padded_text +endfunction + +function! SWC_gen_stars () + let star_count = b:WIN.x * s:STARFIELD_HEIGHT * b:WIN.y / s:STAR_DENSITY + let stars = [] + for n in range(star_count) + let x = RandomNumber(b:WIN.x) + 1 + let y = RandomNumber(s:STARFIELD_HEIGHT * b:WIN.y) + 1 + let stars += [{'y':y,'x':x}] + endfor + return stars +endfunction + +function! SWC_paint_stars (stars) + let max_x = b:WIN.x + for star in a:stars + let line = strpart(getline(star.y) . repeat(" ", max_x), 0, max_x) + let line = substitute(line, '\s\zs\%'.(star.x-1).'c\s\ze\s', '.', '') + call setline(star.y, line) + endfor +endfunction + +" Restore previous external compatibility options +let &cpo = s:save_cpo diff --git a/plugin/autoswap_mac.vim b/plugin/autoswap_mac.vim new file mode 100644 index 0000000..75a0e18 --- /dev/null +++ b/plugin/autoswap_mac.vim @@ -0,0 +1,111 @@ +" Vim global plugin for automating response to swapfiles +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"############################################################# +"## ## +"## Note that this plugin only works for Vim sessions ## +"## running in Terminal on MacOS X. And only if your ## +"## Vim configuration includes: ## +"## ## +"## set title titlestring= ## +"## ## +"## See below for the two functions that would have to be ## +"## rewritten to port this plugin to other OS's. ## +"## ## +"############################################################# + + +" If already loaded, we're done... +if exists("loaded_autoswap_mac") + finish +endif +let loaded_autoswap_mac = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + +" Invoke the behaviour whenever a swapfile is detected... +" +augroup AutoSwap_Mac + autocmd! + autocmd SwapExists * call AS_M_HandleSwapfile(expand(':p')) +augroup END + + +" The automatic behaviour... +" +function! AS_M_HandleSwapfile (filename) + + " Is file already open in another Vim session in some other Terminal window??? + let active_window = AS_M_DetectActiveWindow(a:filename) + + " If so, go there instead and terminate this attempt to open the file... + if (strlen(active_window) > 0) + call AS_M_DelayedMsg('Switched to existing session in another window') + call AS_M_SwitchToActiveWindow(active_window) + let v:swapchoice = 'q' + + " Otherwise, if swapfile is older than file itself, just get rid of it... + elseif getftime(v:swapname) < getftime(a:filename) + call AS_M_DelayedMsg("Old swapfile detected...and deleted") + call delete(v:swapname) + let v:swapchoice = 'e' + + " Otherwise, open file read-only... + else + call AS_M_DelayedMsg("Swapfile detected...opening read-only") + let v:swapchoice = 'o' + endif +endfunction + + +" Print a message after the autocommand completes +" (so you can see it, but don't have to hit to continue)... +" +function! AS_M_DelayedMsg (msg) + " A sneaky way of injecting a message when swapping into the new buffer... + augroup AutoSwap_Mac_Msg + autocmd! + " Print the message on finally entering the buffer... + autocmd BufWinEnter * echohl WarningMsg + exec 'autocmd BufWinEnter * echon "\r'.printf("%-60s", a:msg).'"' + autocmd BufWinEnter * echohl NONE + + " And then remove these autocmds, so it's a "one-shot" deal... + autocmd BufWinEnter * augroup AutoSwap_Mac_Msg + autocmd BufWinEnter * autocmd! + autocmd BufWinEnter * augroup END + augroup END +endfunction + + +"################################################################# +"## ## +"## Rewrite the following two functions to port this plugin ## +"## to other operating systems. ## +"## ## +"################################################################# + +" Return an identifier for a terminal window already editing the named file +" (Should either return a string identifying the active window, +" or else return an empty string to indicate "no active window")... +" +function! AS_M_DetectActiveWindow (filename) + let shortname = fnamemodify(a:filename,":t") + let active_window = system('osascript -e ''tell application "Terminal" to every window whose (name begins with "'.shortname.' " and name ends with "VIM")''') + let active_window = substitute(active_window, '^window id \d\+\zs\_.*', '', '') + return (active_window =~ 'window' ? active_window : "") +endfunction + + +" Switch to terminal window specified... +" +function! AS_M_SwitchToActiveWindow (active_window) + call system('osascript -e ''tell application "Terminal" to set frontmost of '.a:active_window.' to true''') +endfunction + + +" Restore previous external compatibility options +let &cpo = s:save_cpo diff --git a/plugin/betterdigraphs.vim b/plugin/betterdigraphs.vim new file mode 100644 index 0000000..6cea405 --- /dev/null +++ b/plugin/betterdigraphs.vim @@ -0,0 +1,376 @@ +" Vim global plugin for better digraph interactions... +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"############################################################################## +"## ## +"## To use: ## +"## ## +"## inoremap BDG_GetDigraph() ## +"## ## +"############################################################################## +"## ## +"## Digraph naming scheme: ## +"## ## +"## 1. Accented characters ## +"## ## +"## Always the letter itself, followed by ## +"## the first character of the accent's name: ## +"## ## +"## A/a --> acute (e.g. aa --> á YA --> Ý ) ## +"## G/g --> grave (e.g. ag --> à og --> ò ) ## +"## C/c --> circumflex or cedilla (e.g. AC --> Â cc --> ç ) ## +"## U/u --> umlaut (e.g. au --> ä UU --> Ü ) ## +"## T/t --> tilde (e.g. at --> ã nt --> ñ ) ## +"## S/s --> slash (e.g. os --> ø OS --> Ø ) ## +"## R/r --> ring (e.g. AR --> Å ) ## +"## ## +"## ## +"## 2. Unaccented characters ## +"## ## +"## AE --> "[AE] diphthong" --> Æ ## +"## ae --> "[ae] diphthong" --> æ ## +"## TH --> "[TH]ORN] --> Þ ## +"## th --> "[th]orn] --> þ ## +"## EH --> "[E]T[H] --> Ð ## +"## eh --> "[e]t[h] --> ð ## +"## ss --> "long e[ss]" --> ß ## +"## ## +"## ## +"## 3. Non-alphabetics ## +"## (Usually the first letter(s) of each word, except where ambiguous, ## +"## in which case: the first and last letters are used instead.) ## +"## ## +"## ce --> "[ce]nt" --> ¢ ## +"## ci --> "[ci]rcumflex" --> ^ ## +"## co --> "[co]pyright" --> © ## +"## de --> "[de]gree" --> ° ## +"## mu --> "[mu]" --> µ ## +"## no --> "[no]t" --> ¬ ## +"## nu --> "[nu]mber" --> # ## +"## pa --> "[pa]ragraph" --> ¶ ## +"## po --> "[po]und" --> £ ## +"## re --> "[re]gistered" --> ® ## +"## se --> "[se]ction" --> § ## +"## sp --> "[sp]ace" --> ## +"## ti --> "[ti]lde" --> ~ ## +"## ye --> "[ye]n" --> ¥ ## +"## ## +"## as --> "[a]t [s]ign" --> @ ## +"## bb --> "[b]roken [b]ar" --> ¦ ## +"## bs --> "[b]ack [s]lash --> \ ## +"## bt --> "[b]ack [t]ick" --> ` ## +"## ds --> "[d]ivide [s]ign" --> ÷ ## +"## ft --> "[f]orward [t]ick" --> ´ ## +"## ms --> "[m]ultiply [s]ign" --> × ## +"## pm --> "[p]lus or [m]inus" --> ± ## +"## vb --> "[v]ertical [b]ar" --> | ## +"## ## +"## dr --> "[d]olla[r]" --> $ ## +"## dt --> "[d]o[t]" --> · ## +"## ## +"## ## +"## 4. Brackets ## +"## ('l/r' for left/right; 's/c/d' for square/curly/double-angle) ## +"## ## +"## ls --> [ rs --> ] ## +"## lc --> { rc --> } ## +"## ld --> « rd --> » ## +"## ## +"## ## +"## 3. Inverted punctuation ## +"## (Always the character followed by 'i' for inverted) ## +"## ## +"## !i --> ¡ ## +"## ?i --> ¿ ## +"## ## +"## ## +"## 3. Ordinals ## +"## (Always the character followed by 'o') ## +"## ## +"## ao --> ª ## +"## oo --> º ## +"## 1o --> ¹ ## +"## 2o --> ² ## +"## 3o --> ³ ## +"## ## +"## ## +"## 5. Some obvious pictographic alternatives ## +"## (Generally, doubling the letter produces the common variant) ## +"## ## +"## ++ --> ± ## +"## +- --> ± ## +"## xx --> × ## +"## ## +"## << --> « ## +"## >> --> » ## +"## ## +"## !! --> ¡ ## +"## ?? --> ¿ ## +"## ## +"## 11 --> ¹ ## +"## 22 --> ² ## +"## 33 --> ³ ## +"## ## +"## 14 --> ¼ ## +"## 12 --> ½ ## +"## 34 --> ¾ ## +"## ## +"## ## +"############################################################################## + + +" If already loaded, we're done... +if exists("loaded_betterdigraphs") + finish +endif +let loaded_betterdigraphs = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + +" Highlight group that emulates cursor appearance during digraph insertion... +highlight BDG_Cursor_Emulation ctermfg=blue ctermbg=white + +" Highlight group to display hints... +highlight default link BDG_Digraph_Table SpecialKey + + +" How many entries per line in the displayed table??? +let s:ENTRIES_PER_LINE = 6 +let s:INTER_ENTRY_GAP = 2 + + +" Precompute spacings... +let s:ENTRY_SPACING = repeat(" ", 9) +let s:GAP_SPACING = repeat(" ", s:INTER_ENTRY_GAP) +let s:BLANK_LINE = [repeat(s:ENTRY_SPACING . s:GAP_SPACING, s:ENTRIES_PER_LINE) ] + +" This elaboration intercepts the timeouts on regular getchar()... +function! s:active_getchar () + let char = 0 + while !char + let char = getchar() + endwhile + return nr2char(char) +endfunction + +" Retrieve the digraph list (should be called in a :silent)... +function! s:get_digraphs () + redir => digraphs + digraphs + redir END + return substitute(digraphs,'\%d173', '-?','') " Translate invisible soft-hyphen +endfunction + +function! s:show_digraphs (digraphs, cursor_char, context) + " Pad digraph table to fill screen + let digraphs = copy(a:digraphs) + repeat([""], winheight(0)) + + " Display first half of digraph table... + echohl BDG_Digraph_Table + echon join(digraphs[0 : a:context.line-2], "\n") . "\n" + + " Display cursor line with emulated digraph marker... + echohl Normal + echon strpart(a:context.text, 0, a:context.col-1) + echohl BDG_Cursor_Emulation + echon a:cursor_char + echohl Normal + echon strpart(a:context.text, a:context.col-1) . "\n" + + " Display remainder of digraph table... + echohl BDG_Digraph_Table + echon join(digraphs[a:context.line-1 : winheight(0)-2], "\n") . "\n" + echohl None +endfunction + +let g:BDG_filtering = 1 + +" Filter out digraphs that don't start or end with the specified character... +function! s:filter_digraphs (digraphs, char) + if !g:BDG_filtering + return a:digraphs + endif + let digraphs = copy(a:digraphs) + + for line in range(len(digraphs)) + let filtered_line = [] + for digraph_spec in split(digraphs[line], '.\{9}\zs ') + let filtered_spec = substitute(digraph_spec, '\C^.. --> [^'.a:char.'].\s*$', repeat(' ',9), '') + let filtered_spec = substitute(filtered_spec, '\C^.. --> \zs['.a:char.']\ze\S\s*$', ' ', '') + let filtered_line += [filtered_spec] + endfor + let digraphs[line] = join(filtered_line, s:GAP_SPACING) . s:GAP_SPACING + endfor + + return digraphs +endfunction + +" Emulate a more helpful ^K... +function! BDG_GetDigraph () + " Locate cursor... + let context = { 'line': winline(), 'col': wincol(), 'text': getline('.') } + + " Grab list of digraphs... + let digraphs = s:digraph_table + + " Simulate first char of two-character digraph code (with or to escape)... + call s:show_digraphs(digraphs, '?', context) + let char1 = s:active_getchar() + + " Simulate second char of two-character digraph code (with or to escape)... + if (char1 == "\" || char1 == "\") + call feedkeys("\") + return "\\ " + else + call s:show_digraphs(s:filter_digraphs(digraphs, char1), char1, context) + let char2 = s:active_getchar() + endif + + " Return the digraph-constructing sequence... + return get(g:BDG_digraphs, char1.char2, "") + +endfunction + + +" Set up default set of characters if user hasn't specified... +if !exists('g:BDG_digraphs') + let g:BDG_digraphs = { + \ + \ 'sp' : ' ', + \ 'nu' : '#', + \ 'dr' : '$', + \ 'as' : '@', + \ 'bs' : '\', + \ 'ci' : '^', + \ 'bt' : '`', + \ 'vb' : '|', + \ 'ti' : '~', + \ 'ft' : '´', + \ "''" : '´', + \ + \ 'ls' : '[', 'rs' : ']', + \ 'lc' : '{', 'rc' : '}', + \ 'ld' : '«', 'rd' : '»', + \ '<<' : '«', '>>' : '»', + \ + \ '!!' : '¡', '??' : '¿', + \ 'i!' : '¡', 'i?' : '¿', + \ '!i' : '¡', '?i' : '¿', + \ + \ 'ce' : '¢', + \ 'po' : '£', + \ 'ye' : '¥', + \ + \ 'bb' : '¦', + \ 'se' : '§', + \ 'pa' : '¶', + \ 'co' : '©', + \ 're' : '®', + \ 'sh' : '­', + \ + \ 'mu' : 'µ', + \ 'no' : '¬', + \ 'dt' : '·', + \ 'do' : '·', + \ '+-' : '±', + \ 'pm' : '±', + \ 'ms' : '×', + \ 'xx' : '×', + \ 'ds' : '÷', + \ 'di' : '÷', + \ 'de' : '°', + \ + \ 'ao' : 'ª', + \ 'oo' : 'º', + \ '11' : '¹', + \ '22' : '²', + \ '33' : '³', + \ + \ '14' : '¼', + \ '12' : '½', + \ '34' : '¾', + \ + \ 'AG' : 'À', 'EG' : 'È', 'IG' : 'Ì', 'OG' : 'Ò', 'UG' : 'Ù', + \ 'AA' : 'Á', 'EA' : 'É', 'IA' : 'Í', 'OA' : 'Ó', 'UA' : 'Ú', 'YA' : 'Ý', + \ 'AC' : 'Â', 'EC' : 'Ê', 'IC' : 'Î', 'OC' : 'Ô', 'UC' : 'Û', + \ 'AU' : 'Ä', 'EU' : 'Ë', 'IU' : 'Ï', 'OU' : 'Ö', 'UU' : 'Ü', + \ 'AT' : 'Ã', 'OT' : 'Õ', + \ 'AR' : 'Å', + \ 'AE' : 'Æ', 'OS' : 'Ø', + \ + \ 'ag' : 'à', 'eg' : 'è', 'ig' : 'ì', 'og' : 'ò', 'ug' : 'ù', + \ 'aa' : 'á', 'ea' : 'é', 'ia' : 'í', 'oa' : 'ó', 'ua' : 'ú', 'ya' : 'ý', + \ 'ac' : 'â', 'ec' : 'ê', 'ic' : 'î', 'oc' : 'ô', 'uc' : 'û', + \ 'au' : 'ä', 'eu' : 'ë', 'iu' : 'ï', 'ou' : 'ö', 'uu' : 'ü', 'yu' : 'ÿ', + \ 'at' : 'ã', 'ot' : 'õ', + \ 'ar' : 'å', + \ 'ae' : 'æ', 'os' : 'ø', + \ + \ 'CC' : 'Ç', + \ 'cc' : 'ç', + \ + \ 'NT' : 'Ñ', + \ 'NN' : 'Ñ', + \ 'nt' : 'ñ', + \ 'nn' : 'ñ', + \ + \ 'ss' : 'ß', + \ + \ 'TH' : 'Þ', + \ 'th' : 'þ', + \ 'DH' : 'Ð', + \ 'dh' : 'ð', + \} +endif + + +function! s:by_value (v1, v2) + return a:v1[1] < a:v2[1] ? -1 + \ : a:v1[1] > a:v2[1] ? 1 + \ : 0 +endfunction + +" Remove any digraphs that were specified as unwanted... +if exists('g:BDG_remove') + if type(g:BDG_remove) == type([]) + for char in g:BDG_remove + call filter(g:BDG_digraphs, 'v:val != char') + endfor + else + call filter(g:BDG_digraphs, 'v:val !~ g:BDG_remove') + endif +endif + +" Add any extra digraphs that were requested... +if exists('g:BDG_add') + call extend(g:BDG_digraphs, g:BDG_add) +endif + +" Create the display table components... +let s:digraph_table = [] +let current_line = [] +for digraph in sort(items(g:BDG_digraphs), "\by_value") + " Make soft hyphens printable... + let digraph[1] = (digraph[1] == '­') ? '-?' : digraph[1] + + " Construct next entry in table... + let current_line += [printf('%2s', digraph[1]) . ' --> ' . digraph[0]] + + " When next line of table is full, construct the line... + if len(current_line) == s:ENTRIES_PER_LINE + let s:digraph_table += [join(current_line, s:GAP_SPACING) . s:GAP_SPACING] + let current_line = [] + endif +endfor +if len(current_line) + let current_line += repeat([s:ENTRY_SPACING], s:ENTRIES_PER_LINE - len(current_line)) + let s:digraph_table += [join(current_line, s:GAP_SPACING) . s:GAP_SPACING] +endif +let s:digraph_table = s:BLANK_LINE + s:digraph_table + s:BLANK_LINE + + +" Restore previous external compatibility options +let &cpo = s:save_cpo diff --git a/plugin/dragvisuals.vim b/plugin/dragvisuals.vim new file mode 100644 index 0000000..12c4f5d --- /dev/null +++ b/plugin/dragvisuals.vim @@ -0,0 +1,345 @@ +" Vim global plugin for dragging virtual blocks +" Last change: Tue Jul 24 07:19:35 EST 2012 +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"######################################################################### +"## ## +"## Add the following (uncommented) to your .vimrc... ## +"## ## +"## runtime plugin/dragvisuals.vim ## +"## ## +"## vmap DVB_Drag('left') ## +"## vmap DVB_Drag('right') ## +"## vmap DVB_Drag('down') ## +"## vmap DVB_Drag('up') ## +"## vmap D DVB_Duplicate() ## +"## ## +"## " Remove any introduced trailing whitespace after moving... ## +"## let g:DVB_TrimWS = 1 ## +"## ## +"## Or, if you use the arrow keys for normal motions, choose ## +"## four other keys for block dragging. For example: ## +"## ## +"## vmap h DVB_Drag('left') ## +"## vmap l DVB_Drag('right') ## +"## vmap j DVB_Drag('down') ## +"## vmap k DVB_Drag('up') ## +"## ## +"## Or: ## +"## ## +"## vmap DVB_Drag('left') ## +"## vmap DVB_Drag('right') ## +"## vmap DVB_Drag('down') ## +"## vmap DVB_Drag('up') ## +"## ## +"## Or even: ## +"## ## +"## vmap DVB_Drag('left') ## +"## vmap DVB_Drag('right') ## +"## vmap DVB_Drag('down') ## +"## vmap DVB_Drag('up') ## +"## ## +"######################################################################### + + +" If already loaded, we're done... +if exists("loaded_dragvirtualblocks") + finish +endif +let loaded_dragvirtualblocks = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + +"====[ Implementation ]==================================== + +" Toggle this to stop trimming on drags... +if !exists('g:DVB_TrimWS') + let g:DVB_TrimWS = 1 +endif + +function! DVB_Drag (dir) + " No-op in Visual mode... + if mode() ==# 'v' + return "\gv" + + " Do Visual Line drag indirectly via temporary nmap + " (to ensure we have access to block position data)... + elseif mode() ==# 'V' + " Set up a temporary convenience... + exec "nnoremap M \Drag_Lines('".a:dir."')" + + " Return instructions to implement the move and reset selection... + return '"vyM' + + " Otherwise do Visual Block drag indirectly via temporary nmap + " (to ensure we have access to block position data)... + else + " Set up a temporary convenience... + exec "nnoremap M \Drag_Block('".a:dir."')" + + " Return instructions to implement the move and reset selection... + return '"vyM' + endif +endfunction + +" Duplicate selected block and place to the right... +function! DVB_Duplicate () + exec "nnoremap M \DuplicateBlock()" + return '"vyM' +endfunction + +function! s:DuplicateBlock () + nunmap M + " Locate block boundaries... + let [buf_left, line_left, col_left, offset_left ] = getpos("'<") + let [buf_right, line_right, col_right, offset_right] = getpos("'>") + + " Identify special '$' blocks... + let dollar_block = 0 + let start_col = min([col_left+offset_left, col_right+offset_right]) + let end_col = max([col_left+offset_left, col_right+offset_right]) + let visual_width = end_col - start_col + 1 + for visual_line in split(getreg("v"),"\n") + if strlen(visual_line) > visual_width + let dollar_block = 1 + let visual_width = strlen(visual_line) + endif + endfor + let square_up = (dollar_block ? (start_col+visual_width-2).'|' : '') + + set virtualedit=all + return 'gv'.square_up.'yPgv' + \. (visual_width-dollar_block) . 'lo' . (visual_width-dollar_block) . 'l' + \. "y:set virtualedit=block\gv" + \. (dollar_block ? 'o$' : '') +endfunction + + +" Kludge to hide change reporting inside implementation... +let s:NO_REPORT = ":let b:DVB_report=&report\:let &report=1000000000\" +let s:PREV_REPORT = ":let &report = b:DVB_report\" + + +" Drag in specified direction in Visual Line mode... +function! s:Drag_Lines (dir) + " Clean up the temporary convenience... + nunmap M + + " Locate block being shifted... + let [buf_left, line_left, col_left, offset_left ] = getpos("'<") + let [buf_right, line_right, col_right, offset_right] = getpos("'>") + + " Drag entire lines left if possible... + if a:dir == 'left' + " Are all lines indented at least one space??? + let lines = getline(line_left, line_right) + let all_indented = match(lines, '^[^ ]') == -1 + nohlsearch + + " If can't trim one space from start of each line, be a no-op... + if !all_indented + return 'gv' + + " Otherwise drag left by removing one space from start of each line... + else + return s:NO_REPORT + \ . "gv:s/^ //\" + \ . s:PREV_REPORT + \ . "gv" + endif + + " To drag entire lines right, add a space in column 1... + elseif a:dir == 'right' + return s:NO_REPORT + \ . "gv:s/^/ /\:nohlsearch\" + \ . s:PREV_REPORT + \ . "gv" + + " To drag entire lines upwards... + elseif a:dir == 'up' + let EOF = line('$') + + " Can't drag up if at first line... + if line_left == 1 || line_right == 1 + return 'gv' + + " Needs special handling at EOF (because cursor moves up on delete)... + elseif line_left == EOF || line_right == EOF + let height = line_right - line_left + let select_extra = height ? height . 'j' : "" + return s:NO_REPORT + \ . 'gvxP' + \ . s:PREV_REPORT + \ . 'V' . select_extra + + " Otherwise just cut-move-paste-reselect... + else + let height = line_right - line_left + let select_extra = height ? height . 'j' : "" + return s:NO_REPORT + \ . 'gvxkP' + \ . s:PREV_REPORT + \ . 'V' . select_extra + endif + + " To drag entire lines downwards... + elseif a:dir == 'down' + let EOF = line('$') + + " This is how much extra we're going to have to reselect... + let height = line_right - line_left + let select_extra = height ? height . 'j' : "" + + " Needs special handling at EOF (to push selection down into new space)... + if line_left == EOF || line_right == EOF + return "O\gv" + + " Otherwise, just cut-move-paste-reselect... + else + return s:NO_REPORT + \ . 'gvxp' + \ . s:PREV_REPORT + \ . 'V' . select_extra + endif + + endif +endfunction + +" Drag in specified direction in Visual Block mode... +function! s:Drag_Block (dir) + " Clean up the temporary convenience... + nunmap M + + " Locate block being shifted... + let [buf_left, line_left, col_left, offset_left ] = getpos("'<") + let [buf_right, line_right, col_right, offset_right] = getpos("'>") + + " Identify special '$' blocks... + let dollar_block = 0 + let start_col = min([col_left+offset_left, col_right+offset_right]) + let end_col = max([col_left+offset_left, col_right+offset_right]) + let visual_width = end_col - start_col + 1 + for visual_line in split(getreg("v"),"\n") + if strlen(visual_line) > visual_width + let dollar_block = 1 + let visual_width = strlen(visual_line) + endif + endfor + let square_up = (dollar_block ? (start_col+visual_width-2).'|' : '') + + " Drag left... + if a:dir == 'left' + "Can't drag left at left margin... + if col_left == 1 || col_right == 1 + return 'gv' + + " Otherwise reposition one column left (and optionally trim any whitespace)... + elseif g:DVB_TrimWS + " May need to be able to temporarily step past EOL... + let prev_ve = &virtualedit + set virtualedit=all + + " Are we moving past other text??? + let square_up_final = "" + if dollar_block + let lines = getline(line_left, line_right) + if match(lines, '^.\{'.(start_col-2).'}\S') >= 0 + let dollar_block = 0 + let square_up_final = (start_col+visual_width-3).'|' + endif + endif + + let vcol = start_col - 2 + return 'gv'.square_up.'xhP' + \ . s:NO_REPORT + \ . "gvhoho:s/\\s*$//\gv\" + \ . ':set virtualedit=' . prev_ve . "\" + \ . s:PREV_REPORT + \ . ":nohlsearch\gv" + \ . (dollar_block ? '$' : square_up_final ) + else + return 'gv'.square_up.'xhPgvhoho' + endif + + " Drag right... + elseif a:dir == 'right' + " May need to be able to temporarily step past EOL... + let prev_ve = &virtualedit + set virtualedit=all + + " Reposition block one column to the right... + if g:DVB_TrimWS + let vcol = start_col + return 'gv'.square_up.'xp' + \ . s:NO_REPORT + \ . "gvlolo" + \ . ":s/\\s*$//\gv\" + \ . ':set virtualedit=' . prev_ve . "\" + \ . s:PREV_REPORT + \ . (dollar_block ? 'gv$' : 'gv') + else + return 'gv'.square_up.'xp:set virtualedit=' . prev_ve . "\gvlolo" + endif + + " Drag upwards... + elseif a:dir == 'up' + " Can't drag upwards at top margin... + if line_left == 1 || line_right == 1 + return 'gv' + endif + + " May need to be able to temporarily step past EOL... + let prev_ve = &virtualedit + set virtualedit=all + + " If trimming whitespace, jump to just below block to do it... + if g:DVB_TrimWS + let height = line_right - line_left + 1 + return 'gv'.square_up.'xkPgvkoko"vy' + \ . height + \ . 'j:s/\s*$//' + \ . "\:nohlsearch\:set virtualedit=" + \ . prev_ve + \ . "\gv" + \ . (dollar_block ? '$' : '') + + " Otherwise just move and reselect... + else + return 'gv'.square_up.'xkPgvkoko"vy:set virtualedit=' + \ . prev_ve + \ . "\gv" + \ . (dollar_block ? '$' : '') + endif + + " Drag downwards... + elseif a:dir == 'down' + " May need to be able to temporarily step past EOL... + let prev_ve = &virtualedit + set virtualedit=all + + " If trimming whitespace, move to just above block to do it... + if g:DVB_TrimWS + return 'gv'.square_up.'xjPgvjojo"vyk:s/\s*$//' + \ . "\:nohlsearch\:set virtualedit=" + \ . prev_ve + \ . "\gv" + \ . (dollar_block ? '$' : '') + + " Otherwise just move and reselect... + else + return 'gv'.square_up.'xjPgvjojo"vy' + \ . "\:set virtualedit=" + \ . prev_ve + \ . "\gv" + \ . (dollar_block ? '$' : '') + endif + endif +endfunction + + +" Restore previous external compatibility options +let &cpo = s:save_cpo + diff --git a/plugin/hudigraphs.vim b/plugin/hudigraphs.vim new file mode 100644 index 0000000..7270cd5 --- /dev/null +++ b/plugin/hudigraphs.vim @@ -0,0 +1,143 @@ +" Vim global plugin for heads-up digraph interactions... +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"############################################################################## +"## ## +"## To use: ## +"## ## +"## inoremap HUDG_GetDigraph() ## +"## ## +"############################################################################## + + +" If already loaded, we're done... +if exists("loaded_hudigraphs") + finish +endif +let loaded_hudigraphs = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + + +" Highlight group that emulates cursor appearance during digraph insertion... +highlight HUDG_Cursor_Emulation ctermfg=blue ctermbg=white + + +" This elaboration intercepts the timeouts on regular getchar()... +function! s:active_getchar () + let char = 0 + while !char + let char = getchar() + endwhile + return nr2char(char) +endfunction + +" Retrieve the digraph list (should be called in a :silent)... +function! s:get_digraphs () + redir => digraphs + digraphs + redir END + return substitute(digraphs,'\%d173', '-?','') " Translate invisible soft-hyphen +endfunction + +function! s:show_digraphs (digraphs, cursor_char, context) + " Pad digraph table to fill screen + let digraphs = copy(a:digraphs) + repeat(['~'], winheight(0)) + + " Display first half of digraph table... + echon "\n" + echohl SpecialKey + echon join(digraphs[0 : a:context.line-2], "\n") . "\n" + + " Display cursor line with emulated digraph marker... + echohl Normal + echon strpart(a:context.text, 0, a:context.col-1) + echohl HUDG_Cursor_Emulation + echon a:cursor_char + echohl Normal + echon strpart(a:context.text, a:context.col-1) . "\n" + + " Display remainder of digraph table... + echohl SpecialKey + echon join(digraphs[a:context.line-1 : winheight(0)-2], "\n") . "\n" + echohl None +endfunction + +let g:HUDG_filtering = 1 + +" Filter out digraphs that don't start or end with the specified character... +function! s:filter_digraphs (digraphs, char) + if !g:HUDG_filtering + return a:digraphs + endif + let digraphs = copy(a:digraphs) + + for line in range(len(digraphs)) + let filtered_line = [] + for digraph_spec in split(digraphs[line], '.\{9}\zs ') + let filtered_spec = substitute(digraph_spec, '\C^.. --> [^'.a:char.']\{2}\s*$', repeat(' ',9), '') + let filtered_spec = substitute(filtered_spec, '\C^.. --> \S\zs['.a:char.']\ze\s*$', ' ', '') + let filtered_spec = substitute(filtered_spec, '\C^.. --> \zs['.a:char.']\ze\S\s*$', ' ', '') + let filtered_line += [filtered_spec] + endfor + let digraphs[line] = join(filtered_line, ' ') + endfor + + return digraphs +endfunction + +" Rearrange digraph table more usefully... +function! s:get_retabulated_digraphs () + " Get raw data... + silent let digraphs_list = split(s:get_digraphs(), "\n") + + " Convert to table... + let digraphs_table = [] + for line in range(len(digraphs_list)) + let table_line = [] + for digraph_spec in split(digraphs_list[line], '.\{9}\zs ') + let match_parts = matchlist(digraph_spec, '^\(..\) \(\S\{1,2}\)') + if !len(match_parts) + let match_parts = ['','??','?'] + endif + let table_line += [printf("%2s",match_parts[2]) . ' --> ' . match_parts[1]] + endfor + let digraphs_table += [join(table_line, ' ')] + endfor + + return digraphs_table +endfunction + +" Emulate a more helpful ^K... +function! HUDG_GetDigraph () + " Locate cursor... + let context = { 'line': winline(), 'col': wincol(), 'text': getline('.') } + + " Grab list of digraphs... + let digraphs = s:get_retabulated_digraphs() + + " Simulate first char of two-character digraph code (with or to escape)... + call s:show_digraphs(digraphs, '?', context) + let char1 = s:active_getchar() + + " Simulate second char of two-character digraph code (with or to escape)... + if (char1 == "\" || char1 == "\") + let char2 = "" + else + call s:show_digraphs(s:filter_digraphs(digraphs, char1), char1, context) + let char2 = s:active_getchar() + endif + + " Return the digraph-constructing sequence... + return "\".char1.char2 +endfunction + + + + + +" Restore previous external compatibility options +let &cpo = s:save_cpo diff --git a/plugin/listtrans.vim b/plugin/listtrans.vim new file mode 100644 index 0000000..16ef9f8 --- /dev/null +++ b/plugin/listtrans.vim @@ -0,0 +1,121 @@ +" Vim global plugin for list format translations +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"###################################################################### +"## ## +"## To use: ## +"## ## +"## nmap :call ListTrans_toggle_format() ## +"## vmap :call ListTrans_toggle_format('visual') ## +"## ## +"## For example: ## +"## ## +"## nmap ;l :call ListTrans_toggle_format() ## +"## vmap ;l :call ListTrans_toggle_format('visual') ## +"## ## +"###################################################################### + + +" If already loaded, we're done... +if exists("loaded_listtrans") + finish +endif +let loaded_listtrans = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + +" Useful constants... +let g:LT_DEF_LIST_CONJ = 'and' +let g:LT_CONJUNCTIONS = [ +\ 'and\s\+not', 'and', 'plus', 'with', +\ 'or\s\+else', 'or\s\+otherwise', +\ 'or', 'nor', 'but\s\+not', 'but', +\ 'else', 'otherwise' +\] + +" Build the necessary pattern... +let s:LT_CONJ_PAT = join(g:LT_CONJUNCTIONS, '\|') + +" This is the entire interface... +function! ListTrans_toggle_format (...) range + " Extract the target text... + if a:0 + silent normal gvy + else + silent normal vipy + endif + let text = getreg("") + + " Remember the indent and any bullet... + let [match, indent, bullet; etc] = matchlist(text, '^\(\s*\)\([^A-Za-z \t]*\s*\)') + + " If it starts with a bullet, are there more bullets??? + if strlen(bullet) > 0 + let items = split(substitute(text,'^\s*\V'.bullet,'',''), '\n\s*\V'.bullet) + call map(items, 'substitute(v:val,''[[:space:]]\+''," ","g")') + call map(items, 'substitute(v:val,''[[:space:]]$'',"","")') + + " If two or more bulleted items, it's bullet list --> text list... + if len(items) >= 2 + " Infer the correct conjunction... + let has_conj = matchlist(items[-2], '^\(\_.\{-}\)[,;]\?\s*\('.s:LT_CONJ_PAT.'\)\s*$') + let [items[-2], conj] = len(has_conj) ? has_conj[1:2] : [items[-2], g:LT_DEF_LIST_CONJ] + + " Conjoin the various items (using Oxford rules)... + if len(items) == 2 + let sep = len(filter(copy(items), 'v:val =~ ","')) ? ', ' : ' ' + let reformatted_text = indent . join(items, sep.conj.' ') + else + let sep = len(filter(copy(items), 'v:val =~ ","')) ? '; ' : ', ' + let reformatted_text = indent . join(items[0:-2], sep) + \ . sep . conj . ' ' + \ . items[-1] + endif + + " Paste back into buffer in place of original... + call setreg("", reformatted_text, mode()) + silent normal gvp + return + endif + endif + + " Otherwise, it's text list --> bullet list... + + " Identify and remove initial indent... + let [match, indent, text; etc] = matchlist(text, '^\(\s*\)\(\_.*\)') + + " Minimize whitespace... + let text = substitute(text,'[[:space:]]\+'," ",'g') + let text = substitute(text,'[[:space:]]\+$','','') + + " Identify most likely separator... + let sep = text =~ ';' ? '\s*;\s*' + \ : text =~ ',' ? '\s*,\s*' + \ : '\s\+\(' . s:LT_CONJ_PAT . '\)\s\+' + + " Separate... + let items = split(text, sep) + + " Check for an extra conjunction in the last item... + let last_item = remove(items, -1) + let last_sep = matchstr(last_item, '^\s*\(' . s:LT_CONJ_PAT . '\)\>\s*') + if strlen(last_sep) + let [last_item] = split(last_item, '\s*\(' . s:LT_CONJ_PAT . '\)\s\+') + if last_sep !~ '^\s*\(and\|plus\)' + let items[-1] .= ', ' . last_sep + endif + endif + let items += [last_item] + + " Rejoin and paste back into buffer in place of original... + let reformatted_text = join(map(items, 'indent."- ".v:val."\n"'), "") + call setreg("", reformatted_text, mode()) + silent normal gvp + +endfunction + +" Restore previous external compatibility options +let &cpo = s:save_cpo diff --git a/plugin/vmath.vim b/plugin/vmath.vim new file mode 100644 index 0000000..d289795 --- /dev/null +++ b/plugin/vmath.vim @@ -0,0 +1,140 @@ +" Vim global plugin for math on visual regions +" Maintainer: Damian Conway +" License: This file is placed in the public domain. + +"###################################################################### +"## ## +"## To use: ## +"## ## +"## vmap ++ VMATH_YankAndAnalyse() ## +"## nmap ++ vip++ ## +"## ## +"## (or whatever keys you prefer to remap these actions to) ## +"## ## +"###################################################################### + + +" If already loaded, we're done... +if exists("loaded_vmath") + finish +endif +let loaded_vmath = 1 + +" Preserve external compatibility options, then enable full vim compatibility... +let s:save_cpo = &cpo +set cpo&vim + +" Grab visual selection and do simple math on it... +function! VMATH_YankAndAnalyse () + return "y:call VMATH_Analyse()\gv" +endfunction + +" What to consider a number... +let s:NUM_PAT = '^[+-]\?\d\+\%([.]\d\+\)\?\([eE][+-]\?\d\+\)\?$' + +" How widely to space the report components... +let s:REPORT_GAP = 5 "spaces between components + +" Do simple math on current yank buffer... +function! VMATH_Analyse () + " Extract data from selection... + let selection = getreg('') + let raw_numbers = filter(split(selection), 'v:val =~ s:NUM_PAT') + let numbers = map(copy(raw_numbers), 'str2float(v:val)') + + " Results include a newline if original selection did... + let newline = selection =~ "\n" ? "\n" : "" + + " Calculate and en-register various interesting metrics... + let summation = len(numbers) ? join( numbers, ' + ') : '0' + call setreg('s', s:tidy( eval( summation ) )) " Sum --> register s + call setreg('a', s:average(raw_numbers) ) " Average --> register a + call setreg('x', s:tidy( s:max(numbers) )) " Max --> register x + call setreg('n', s:tidy( s:min(numbers) )) " Min --> register n + call setreg('r', @n . ' to ' . @x ) " Range --> register r + + " Default paste buffer should depend on original contents (TODO) + call setreg('', @s ) + + " Report... + let gap = repeat(" ", s:REPORT_GAP) + highlight NormalUnderlined term=underline cterm=underline gui=underline + echohl NormalUnderlined + echo 's' + echohl NONE + echon 'um: ' . @s . gap + echohl NormalUnderlined + echon 'a' + echohl NONE + echon 'vg: ' . @a . gap + echon 'mi' + echohl NormalUnderlined + echon 'n' + echohl NONE + echon ': ' . @n . gap + echon 'ma' + echohl NormalUnderlined + echon 'x' + echohl NONE + echon ': ' . @x . gap + +endfunction + +" Prettify numbers... +function! s:tidy (number) + let tidied = printf('%g', a:number) + return substitute(tidied, '[.]0\+$', '', '') +endfunction + +" Compute average with meaningful number of decimal places... +function! s:average (numbers) + " Compute average... + let summation = eval( len(a:numbers) ? join( a:numbers, ' + ') : '0' ) + let avg = 1.0 * summation / s:max([len(a:numbers), 1]) + + " Determine significant figures... + let min_decimals = 15 + for num in a:numbers + let decimals = strlen(matchstr(num, '[.]\d\+$')) - 1 + if decimals < min_decimals + let min_decimals = decimals + endif + endfor + + " Adjust answer... + return min_decimals > 0 ? printf('%0.'.min_decimals.'f', avg) + \ : string(avg) +endfunction + +" Reimplement these because the builtins don't handle floats (!!!) +function! s:max (numbers) + if !len(a:numbers) + return 0 + endif + let numbers = copy(a:numbers) + let maxnum = numbers[0] + for nextnum in numbers[1:] + if nextnum > maxnum + let maxnum = nextnum + endif + endfor + return maxnum +endfunction + +function! s:min (numbers) + if !len(a:numbers) + return 0 + endif + let numbers = copy(a:numbers) + let minnum = numbers[0] + for nextnum in numbers[1:] + if nextnum < minnum + let minnum = nextnum + endif + endfor + return minnum +endfunction + + +" Restore previous external compatibility options +let &cpo = s:save_cpo diff --git a/spell/en-basic.latin1.spl b/spell/en-basic.latin1.spl new file mode 100644 index 0000000..61f4004 Binary files /dev/null and b/spell/en-basic.latin1.spl differ