1"= 2" cream-addon.vim 3" 4" Cream -- An easy-to-use configuration of the famous Vim text editor 5" [ http://cream.sourceforge.net ] Copyright (C) 2001-2011 Steve Hall 6" 7" License: 8" This program is free software; you can redistribute it and/or modify 9" it under the terms of the GNU General Public License as published by 10" the Free Software Foundation; either version 2 of the License, or 11" (at your option) any later version. 12" [ http://www.gnu.org/licenses/gpl.html ] 13" 14" This program is distributed in the hope that it will be useful, but 15" WITHOUT ANY WARRANTY; without even the implied warranty of 16" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17" General Public License for more details. 18" 19" You should have received a copy of the GNU General Public License 20" along with this program; if not, write to the Free Software 21" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 22" 02111-1307, USA. 23" 24 25" README {{{1 26" Description: 27" 28" A Cream add-on is simply a Vim script written to add functionality 29" not provided in the standard Cream distribution. Through these 30" add-ons, a wide range of functionality can be made available to the 31" user through both menus and key mappings. 32" 33" o See Cream_addon_list() for how to register an add-on. 34" 35" o Automatically loaded from the cream/addons/ subdirectory. 36" 37" o Placed into a submenu in the Tools menu. 38" 39" o Each add-on must provide a single functionality in the form of 40" a function call made through either the menu or the optional key 41" mapping if the user opts to map it. 42" 43" o 6 variations of F12 key are available for the user to map: F12, 44" Shift+F12, Ctl+F12, Alt+F12, Ctrl+Shift+F12 and Alt+Shift+F12. 45" (Ctrl+Alt+F12 unused due to typical GNU/Linux window manager 46" restrictions). 47" 48" Notes: 49" 50" o Should we check to ensure there are no duplicate functions? 51" 52" o This is a pretty simple scheme. Someone with even moderate 53" knowledge of the Vim scripting language can theoretically do most 54" anything with it since we automatically load the script, put it in 55" the menu and execute the call statement. Much the same way Vim 56" plugins are flexible and powerful, so are add-ons. 57" 58" o Functionality may depend on the available Cream environment. But 59" we suggest you supply your own functions unless you keep us up to 60" speed on what you're depending on. 61" 62" o Obviously, each function should act as if called from a standing 63" normal mode. You should never use <Esc> within Vim script because 64" it has different implications depending on whether insertmode has 65" been set or not. *Always select your intended mode, never assume 66" a user is in a particular one.* 67" 68" o It would be trivial to devise a massively intertwined set of 69" libraries with multiple files to create a whole suite of 70" functions. Talk to us if you're so inclined, that's crazy. ;) 71" 72" o All functions should be script-scoped (s:) except for the one 73" called. 74" 75" o Developers should use keys intuitively 76" 77" o Developers can provide extensively more flexibility through dialog 78" choices, not key mappings. 79" 80" o Some existing Cream tools should probably be here: 81" * Capitalize? 82" * Whitespace handling? 83" * Cream_debug() 84" * Backup 85" 86 87" Cream_addon_loadall() {{{1 88function! Cream_addon_loadall() 89 90 " Default: 91 call s:Cream_addon_loader($CREAM . 'addons/*.vim') 92 " User: (only if exist) 93 if exists("g:cream_user") 94 call s:Cream_addon_loader(g:cream_user . 'addons/*.vim') 95 endif 96 97endfunction 98function! s:Cream_addon_loader(path) 99" load add-ons 100 101 " get file list 102 let myaddons = glob(a:path) 103 if strlen(myaddons) > 0 104 let myaddons = myaddons . "\n" 105 else 106 " none found, quit 107 return 108 endif 109 110 " source each item (destroys array in process) 111 let i = 0 112 let max = MvNumberOfElements(myaddons, "\n") 113 while i < max 114 let idx = stridx(myaddons, "\n") 115 let myaddon = strpart(myaddons, 0, idx) 116 call Cream_source(myaddon) 117 let myaddons = strpart(myaddons, idx + 1) 118 " *** we're not establishing g:cream_addons here! Each add-on 119 " loads itself!! *** 120 let i = i + 1 121 endwhile 122 123endfunction 124 125" Cream_addon_register() {{{1 126function! Cream_addon_register(name, tag, summary, menu, icall, ...) 127" called by each add-on to register: 128" 129" call Cream_addon_list("{name}","{tag}","{summary}","{menu}","{icall}","{vcall}") 130" 131" o Name 132" o Tag is a brief one-line, 40-character descripter 133" o Summary should briefly describe usage. 134" o Menu name as it should apear in the menu (no accelerator) 135" o Insert mode function call or execute statement that Cream will 136" run on menu select or key press. 137" * Optional: Use '<Nil>' to eliminate insertmode call/map. 138" Requires valid visual mode call then. 139" o Visual mode (optional) functon call if the function behaves 140" differently from Insert and Visual modes. If not provided, 141" no visual call is made available. '<Nil>' also excepted. 142" 143" This call loads the array into a global structure of add-ons, and 144" also manages and retains the user-defined key mappings. 145" 146 147 " visual call specified 148 if a:0 == 1 149 let myvcall = a:1 150 else 151 let myvcall = '<Nil>' 152 endif 153 154 " validate 155 " cannot have empty values 156 if a:name == "" 157 call confirm( 158 \ "Error: Required \"name\" missing to list add-on--not loaded.\n" . 159 \ "\n", "&Ok", 1, "Info") 160 return -1 161 elseif a:tag == "" 162 call confirm( 163 \ "Error: Required parameter \"tag\" missing to list add-on \"" . a:name . "\"--not loaded.\n" . 164 \ "\n", "&Ok", 1, "Info") 165 return -1 166 elseif a:summary == "" 167 call confirm( 168 \ "Error: Required parameter \"summary\" missing to list add-on \"" . a:name . "\"--not loaded.\n" . 169 \ "\n", "&Ok", 1, "Info") 170 return -1 171 elseif a:menu == "" 172 call confirm( 173 \ "Error: Required parameter \"menu\" missing to list add-on \"" . a:name . "\"--not loaded.\n" . 174 \ "\n", "&Ok", 1, "Info") 175 return -1 176 elseif a:icall == "" 177 call confirm( 178 \ "Error: Required parameter \"icall\" missing to list add-on \"" . a:name . "\"--not loaded.\n" . 179 \ "\n", "&Ok", 1, "Info") 180 return -1 181 endif 182 183 "---------------------------------------------------------------------- 184 " escape dis-allowed characters 185 " (nah, let them eat cake for now) 186 187 "" validate 188 "let myname = substitute(a:name, "[\n\t\r]", '', 'g') 189 "let mytag = substitute(a:tag, "[\n\t\r]", '', 'g') 190 191 "let mysummary = a:summary 192 ""let mysummary = substitute(mysummary, "[\r]", "\n", 'g') 193 ""let mysummary = substitute(mysummary, "\\[!nt]", "\\\\", 'g') 194 195 "" escape 196 "let mysummary = substitute(mysummary, '\\', '\\\\', 'g') 197 198 "" un-escape desired escapes 199 "" * Anything not recovered here is escaped forever ;) 200 "let mysummary = substitute(mysummary, '\\\\n', '\\n', 'g') 201 "let mysummary = substitute(mysummary, '\\\\r', '\\n', 'g') 202 "let mysummary = substitute(mysummary, '\\\\t', '\\t', 'g') 203 " 204 "" escape slashes (thwarts substitute!) 205 "let mysummary = substitute(mysummary, '/', '\\/', 'g') 206 "---------------------------------------------------------------------- 207 208 " compose this add-on's sub-array 209 "let myitem = a:name . "\t" . a:tag . "\t" . a:summary . "\t" . a:menu . "\t" . a:icall . "\t" . myvcall . "\t" 210 if !exists("g:cream_addons{0}") 211 let g:cream_addons{0} = 1 212 else 213 let g:cream_addons{0} = g:cream_addons{0} + 1 214 endif 215 let g:cream_addons{g:cream_addons{0}}_{1} = a:name 216 let g:cream_addons{g:cream_addons{0}}_{2} = a:tag 217 let g:cream_addons{g:cream_addons{0}}_{3} = a:summary 218 let g:cream_addons{g:cream_addons{0}}_{4} = a:menu 219 let g:cream_addons{g:cream_addons{0}}_{5} = a:icall 220 let g:cream_addons{g:cream_addons{0}}_{6} = myvcall 221 222endfunction 223 224function! Cream_addon_sort() 225 "*** BROKEN: too much memory, crash 226 "let g:cream_addons = MvQSortElements(g:cream_addons, '\n', 'CmpAddonMenu', 1) 227 "*** 228endfunction 229function! CmpAddonMenu(item1, item2, direction) 230" compares menu components of two addon registries 231 232 " get menu item (4th, index 3) of each 233 let mymenu1 = MvElementAt(a:item1, "\t", 3) + 0 234 let mymenu2 = MvElementAt(a:item2, "\t", 3) + 0 235 236 ""*** DEBUG: 237 "let n = confirm( 238 " \ "DEBUG:\n" . 239 " \ " mymenu1 = \"" . mymenu1 . "\"\n" . 240 " \ " mymenu2 = \"" . mymenu2 . "\"\n" . 241 " \ "\n", "&Ok\n&Cancel", 1, "Info") 242 "if n != 1 243 " return 244 "endif 245 ""*** 246 247 if mymenu1 < mymenu2 248 return -a:direction 249 elseif mymenu1 > mymenu2 250 return a:direction 251 else 252 return 0 253 endif 254endfunction 255 256function! Cream_addon_menu() 257 258 let i = 0 259 while i < g:cream_addons{0} 260 let i = i + 1 261 262 let mymenu = g:cream_addons{i}_{4} 263 let myicall = g:cream_addons{i}_{5} 264 let myvcall = g:cream_addons{i}_{6} 265 266 "................................................................... 267 " menu priority number according to it's name, first two chars: 268 " decimal value of char 1 * 26^3 269 " + decimal value of char 2 * 26^2 270 " Notes: 271 " o This is base26 (26^2 * [1-26] is the largest value we can 272 " fit in Vim's self-imposed 32000 (16-bit) priority menu 273 " limit) 274 " o No sorting happens below submenus 275 " 276 let charcnt = 2 277 let menupriority = 1 278 " we might destroy the name in prioritizing (e.g., removing 279 " ampersands) 280 let mymenumunge = mymenu 281 let j = 1 282 let max = charcnt 283 if strlen(mymenumunge) < charcnt 284 let max = strlen(mymenumunge) 285 endif 286 " get each char 287 while j <= max 288 289 " get 26 * j 290 let power = 1 291 let h = charcnt 292 let charval = 0 293 while h > j 294 let power = power * 26 295 let h = h - 1 296 endwhile 297 298 " validate char (keep removing them till one falls in 299 " range) 300 let valid = 0 301 while valid == 0 302 let mycharval = char2nr(mymenumunge[j-1]) 303 304 " A-Z 305 if mycharval >= 65 306 \ && mycharval <= 90 307 break 308 " period 309 elseif mycharval == 46 310 break 311 " a-z 312 elseif mycharval >= 97 313 \ && mycharval <= 122 314 break 315 else 316 " remove the offending characters 317 let mymenumunge = strpart(mymenumunge, 0, j-1) . strpart(mymenumunge, j) 318 endif 319 320 endwhile 321 322 " get value 323 " Note: we're converting A-Z to 1-26 324 if char2nr(mymenumunge[j-1]) == 32 325 " space (highest, makes broken word first) 326 let charval = 27 327 elseif char2nr(mymenumunge[j-1]) == 46 328 " period (quit) 329 break 330 else 331 " else (whatever passed through filter above) 332 let charval = (toupper(char2nr(mymenumunge[j-1]))-64) 333 endif 334 335 let value = charval * power 336 let menupriority = menupriority + value 337 338""*** DEBUG: 339"let n = confirm( 340" \ "DEBUG:\n" . 341" \ " mymenu = \"" . mymenu . "\"\n" . 342" \ " mymenumunge = \"" . mymenumunge . "\"\n" . 343" \ " i = \"" . i . "\"\n" . 344" \ " j = \"" . j . "\"\n" . 345" \ " h = \"" . h . "\"\n" . 346" \ " mymenumunge[j-1] = \"" . mymenumunge[j - 1] . "\"\n" . 347" \ " char2nr(mymenumunge[j - 1]) = \"" . char2nr(mymenumunge[j - 1]) . "\"\n" . 348" \ " toupper(char2nr(mymenumunge[j -1])) = \"" . toupper(char2nr(mymenumunge[j - 1])) . "\"\n" . 349" \ " (toupper(char2nr(mymenumunge[j-1]))-64) = \"" . (toupper(char2nr(mymenumunge[j-1]))-64) . "\"\n" . 350" \ " charval = \"" . charval . "\"\n" . 351" \ " power = \"" . power . "\"\n" . 352" \ " value = \"" . value . "\"\n" . 353" \ " menupriority = \"" . menupriority . "\"\n" . 354" \ "\n", "&Ok\n&Cancel", 1, "Info") 355"if n != 1 356" return 357"endif 358""*** 359 let j = j + 1 360 endwhile 361 "................................................................... 362 363 " make sure spaces and slashes in name are escaped 364 let mymenu = escape(mymenu, ' \') 365 366 " add menus 367 " (a:icall cannot be empty, validated above) 368 " both '<Nil>', skip 369 if myicall ==? '<Nil>' && myvcall ==? '<Nil>' 370 return 371 " icall live (vcall dead menu) 372 elseif myvcall ==? '<Nil>' 373 execute 'imenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' <C-b>:' . myicall . '<CR>' 374 execute 'vmenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . " :\<C-u>call confirm(\"Option not available with selection.\", \"&Ok\", 1, \"Info\")\<CR>" 375 " vcall live (icall dead menu) (could show inactive without selection?) 376 elseif myicall ==? '<Nil>' 377 execute 'imenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . " \<C-b>:call confirm(\"Option not available without selection.\", \"&Ok\", 1, \"Info\")\<CR>" 378 execute 'vmenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' :' . myvcall . '<CR>' 379 " both live 380 else 381 execute 'imenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' <C-b>:' . myicall . '<CR>' 382 " Note: myvcall must be complete mapping. Remember that 383 " a <C-u> should be passed if range is not desired. 384 execute 'vmenu <silent> 70.910.' . menupriority . ' &Tools.&Add-ons.' . mymenu . ' :' . myvcall . '<CR>' 385 endif 386 endwhile 387 388endfunction 389 390" Cream_addon_select() {{{1 391function! Cream_addon_select() 392" * Compose elements to be posted in s:Cream_addon_map_dialog() 393 394 " condition maximum displayed 395 let max = 7 396 let addoncount = g:cream_addons{0} 397 if addoncount < max 398 " if less than max 399 let max = addoncount 400 endif 401 402 " current item begins in the middle of the height 403 let current = max / 2 404 405 " set maximum line length 406 let maxline = 70 407 408 " display dialog 409 let quit = 0 410 while quit == 0 411 412 "---------------------------------------------------------------------- 413 " compose dialog 414 415 " compose selection and description areas 416 let selection = "" 417 let description = "" 418 419 " wrap at beginning 420 if current < 0 421 let current = addoncount - 1 422 " wrap at end 423 elseif current > addoncount - 1 424 let current = 0 425 endif 426 427 "" First Item Displayed Is Half The Diplay Height Before Current 428 "let Addon = Current - ( Max / 2 ) 429 "" Catch Error When Current <= 1/4 Max To List Start 430 "if Current <= ( ( Max / 2 ) / 2 ) 431 " Let Addon = Addoncount - ( Max / 2 ) + Current 432 "endif 433 434 " first item displayed is half the diplay height before current 435 let addon = current - ( max / 2 ) 436 " catch error when current <= 1/4 max to list start 437 if addon < 0 438 let addon = addon + addoncount 439 endif 440 441 " compose each add-on 442 let i = 0 443 while i < max 444 445 " wrap starting item 446 if addon < 0 447 let addon = addoncount - 1 448 elseif addon > addoncount - 1 449 let addon = 0 450 endif 451 452 " get specific elements of item 453 let myaddonname = g:cream_addons{addon+1}_{1} 454 let myaddontag = g:cream_addons{addon+1}_{2} 455 let myaddonsummary = g:cream_addons{addon+1}_{3} 456 "let myaddonmenu = g:cream_addons{addon+1}_{4} 457 "let myaddonicall = g:cream_addons{addon+1}_{5} 458 "let myaddonvcall = g:cream_addons{addon+1}_{6} 459 460 " truncate name and tag so we fit consistently 461 let myname = myaddonname 462 if strlen(myname) > 25 463 let myname = strpart(myname, 0, 22) . "..." 464 endif 465 let mytag = myaddontag 466 if strlen(myname . mytag) > maxline - 3 467 let mytag = strpart(mytag, 0, maxline - strlen(myname)) . "..." 468 endif 469 " beef each up slightly for nicer margins 470 while strlen(myname . mytag) < maxline + 10 471 let mytag = mytag . " " 472 endwhile 473 474 " truncate summary so we fit in the box consistently 475 let mysummary = myaddonsummary 476 if strlen(mysummary) > maxline * 9 477 let mysummary = strpart(mysummary, 0, maxline * 9 - 3) . "..." 478 endif 479 " insert newlines to paragraph summary 480 let remaining = mysummary 481 let mynewstr = "" 482 while strlen(remaining) > maxline 483 " truncate at a space 484 let j = 0 485 " find space 486 while strpart(remaining, maxline - j, 1) != " " 487 let j = j + 1 488 " hmm... didn't find a space, just slash at maxline 489 if j == maxline 490 let j = 0 491 break 492 endif 493 endwhile 494 " now section there and add newline (add 1 to keep the space outboard) 495 let mynewstr = mynewstr . strpart(remaining, 0, maxline - j + 1) . "\n" 496 " remaining is from there beyond 497 let remaining = strpart(remaining, maxline - j + 1) 498 endwhile 499 " get remainder 500 let mysummary = mynewstr . strpart(remaining, 0) . "\n" 501 " make sure each summary has the same number of lines for good looks ;) 502 " (this is an abuse of this function, but it works!) 503 while MvNumberOfElements(mysummary, "\n") < 4 504 let mysummary = mysummary . "\n" 505 endwhile 506 507 " add selection indicators for current 508 if addon == current 509 let selection = selection . ">> " . myname . " -- " . mytag . "\n" 510 " title with current add-on's name 511 let description = "Description: " . myaddonname . "\n" . mysummary 512 else 513 let selection = selection . " " . myname . " -- " . mytag . "\n" 514 endif 515 516 let addon = addon + 1 517 let i = i + 1 518 endwhile 519 520 " remove iterations above 521 let addon = addon - max 522 523 "---------------------------------------------------------------------- 524 " post dialog accounting 525 let n = s:Cream_addon_map_dialog(selection, description) 526 if n == "prev" 527 let current = current - 1 528 let addon = addon - 1 529 continue 530 elseif n == "next" 531 let current = current + 1 532 let addon = addon + 1 533 continue 534 elseif n == "map" 535 536 " get map key 537 let mykey = s:Cream_addon_getkey() 538 539 " if none selected, drop back to select dialog again 540 if mykey == -1 541 continue 542 endif 543 544 " get mapping sub-elements 545 let icall = g:cream_addons{current+1}_{5} 546 let vcall = g:cream_addons{current+1}_{6} 547 548 " map 549 let n = s:Cream_addon_maps(mykey, icall, vcall) 550 551 " retain across sessions if valid 552 if n == 1 553 " if key matches, stick into right array position so 554 " we never have to sort 555 if mykey == '<F12>' 556 let mykeyno = 1 557 elseif mykey == '<S-F12>' 558 let mykeyno = 2 559 elseif mykey == '<C-F12>' 560 let mykeyno = 3 561 elseif mykey == '<M-F12>' 562 let mykeyno = 4 563 elseif mykey == '<C-S-F12>' 564 let mykeyno = 5 565 elseif mykey == '<M-S-F12>' 566 let mykeyno = 6 567 elseif mykey == '<C-M-F12>' 568 let mykeyno = 7 569 elseif mykey == '<C-M-S-F12>' 570 let mykeyno = 8 571 endif 572 if exists("mykeyno") 573 let g:CREAM_ADDON_MAPS{mykeyno}_{1} = mykey 574 let g:CREAM_ADDON_MAPS{mykeyno}_{2} = icall 575 let g:CREAM_ADDON_MAPS{mykeyno}_{3} = vcall 576 unlet mykeyno 577 endif 578 endif 579 580 elseif n == "unmap" 581 " go to unmap routine 582 let quit = Cream_addon_unmap_select() 583 584 elseif n == -1 585 " quit 586 let quit = 1 587 endif 588 589 endwhile 590 591 return 1 592 593endfunction 594 595" Cream_addon_map_dialog() {{{1 596function! s:Cream_addon_map_dialog(selections, description) 597" * Prompt user to select an addon through dialog 598" * Return key 599" 600" +------------------------------------------------------+ 601" | Item 1 -- Encrypt. | 602" | > Item 2 -- Color everything green. < | 603" | Item 3 -- Color everything blue. | 604" | ----------------------------------------------- | 605" | | 606" | Simply select whatever you wish to highlight | 607" | green, and press Ok! | 608" | | 609" | | 610" | +--------+ +--------+ +-------+ +-------+ +--------+ | 611" | | Up | | Down | | Map | | UnMap | | Cancel | | 612" | +--------+ +--------+ +-------+ +-------+ +--------+ | 613" +------------------------------------------------------+ 614 615 let sav_go = &guioptions 616 set guioptions+=v 617 618 " remember focus 619 if !exists("s:focus_map") 620 let s:focus_map = 2 621 endif 622 623 let key = "" 624 let n = confirm( 625 \ a:selections . "\n" . 626 \ "\---------------------------------------------------------------------------------------------------------------------------- \n" . 627 \ a:description . "\n" . 628 \ "\n", "&Up\n&Down\n&Map\nU&nMap...\n&Quit", s:focus_map, "Info") 629 if n == 1 630 let myreturn = "prev" 631 let s:focus_map = 1 632 elseif n == 2 633 let myreturn = "next" 634 let s:focus_map = 2 635 elseif n == 3 636 let myreturn = "map" 637 let s:focus_map = 2 638 elseif n == 4 639 let myreturn = "unmap" 640 let s:focus_map = 2 641 else 642 let myreturn = -1 643 let s:focus_map = 2 644 endif 645 646 " restore and return 647 let &guioptions = sav_go 648 return myreturn 649 650endfunction 651 652" Cream_addon_getkey() {{{1 653function! s:Cream_addon_getkey() 654" Return requested mapping 655" 656" +---------------------------------------------------------------------------------+ 657" | | 658" | Select key (or combination) to map add-on to... | 659" | | 660" | | 661" | ......... ......... ......... ......... .............. ............. .......... | 662" | : F12 : : +Shft : : +Ctrl : : +Alt : : +Shft+Ctrl : : +Shft+Alt : : Cancel : | 663" | :.......: :.......: :.......: :.......: :............: :...........: :........: | 664" +---------------------------------------------------------------------------------+ 665" 666 667 let sav_go = &guioptions 668 set guioptions+=v 669 670 let n = confirm( 671 \ "Select key (or combination) to map add-on to...\n" . 672 \ "\n", "&F12\nShift+F12\nCtrl+F12\nAlt+F12\nCtrl+Shift+F12\nAlt+Shift+F12\nCtrl+Alt+F12\nCtrl+Alt+Shift+F12\n&Cancel", 1, "Info") 673 if n == 1 674 let myreturn = '<F12>' 675 elseif n == 2 676 let myreturn = '<S-F12>' 677 elseif n == 3 678 let myreturn = '<C-F12>' 679 elseif n == 4 680 let myreturn = '<M-F12>' 681 elseif n == 5 682 let myreturn = '<C-S-F12>' 683 elseif n == 6 684 let myreturn = '<M-S-F12>' 685 elseif n == 7 686 let myreturn = '<C-M-F12>' 687 elseif n == 8 688 let myreturn = '<C-M-S-F12>' 689 else 690 let myreturn = -1 691 endif 692 693 " restore and return 694 let &guioptions = sav_go 695 return myreturn 696 697endfunction 698 699" Cream_addon_unmap_select() {{{1 700function! Cream_addon_unmap_select() 701" * Compose elements to be posted in s:Cream_addon_unmap_dialog() 702 703 " initialize to first item (0-based) 704 let current = 0 705 706 " list of mappings to display 707 let mapdisplay = "" 708 709 " main event loop 710 let quit = 0 711 while quit == 0 712 713 " wrap at beginning 714 if current < 0 715 let current = 7 716 " wrap at end 717 elseif current > 7 718 let current = 0 719 endif 720 721 " compose 722 let mapdisplay = "UNMAP:\n\n\n" 723 " Note: keys are 1-based! 724 let i = 0 725 while i < 8 726 "if exists("g:CREAM_ADDON_MAPS{i + 1}_{1}") 727 " let mykey = g:CREAM_ADDON_MAPS{i + 1}_{1} 728 "endif 729 if exists("g:CREAM_ADDON_MAPS{i + 1}_{2}") 730 let myicall = g:CREAM_ADDON_MAPS{i + 1}_{2} 731 endif 732 if exists("g:CREAM_ADDON_MAPS{i + 1}_{3}") 733 let myvcall = g:CREAM_ADDON_MAPS{i + 1}_{3} 734 endif 735 " display each key, even if unmapped 736 if i == 0 737 let tmpkey = '<F12>: ' 738 elseif i == 1 739 let tmpkey = '<S-F12>: ' 740 elseif i == 2 741 let tmpkey = '<C-F12>: ' 742 elseif i == 3 743 let tmpkey = '<M-F12>: ' 744 elseif i == 4 745 let tmpkey = '<C-S-F12>: ' 746 elseif i == 5 747 let tmpkey = '<M-S-F12>: ' 748 elseif i == 6 749 let tmpkey = '<C-M-F12>: ' 750 elseif i == 7 751 let tmpkey = '<C-M-S-F12>: ' 752 endif 753 " convert key mapping to human-readable form just for 754 " dialog 755 let tmpkey = substitute(tmpkey, '[<>]', '', 'g') 756 let tmpkey = substitute(tmpkey, 'C-', 'Ctrl + ', 'g') 757 let tmpkey = substitute(tmpkey, 'M-', 'Alt + ', 'g') 758 let tmpkey = substitute(tmpkey, 'S-', 'Shift + ', 'g') 759 " highlight first line 760 if i == current 761 let mapdisplay = mapdisplay . " >> " . tmpkey 762 else 763 let mapdisplay = mapdisplay . " " . tmpkey 764 endif 765 " info lines for mapping explaining current calls 766 if myicall ==? '<Nil>' 767 let mapdisplay = mapdisplay . myvcall . "\n" 768 else 769 let mapdisplay = mapdisplay . myicall . "\n" 770 endif 771 772 let i = i + 1 773 endwhile 774 " pad bottom of text to even map dialog 775 while MvNumberOfElements(mapdisplay, "\n") < 11 776 let mapdisplay = mapdisplay . "\n" 777 endwhile 778 779 " post dialog accounting 780 let n = s:Cream_addon_unmap_dialog(mapdisplay) 781 if n == "prev" 782 let current = current - 1 783 continue 784 elseif n == "next" 785 let current = current + 1 786 continue 787 elseif n == "unmap" 788 789 " get element's key 790 let mykey = g:CREAM_ADDON_MAPS{current + 1}_{1} 791 792 " if already empty, just go back to select dialog 793 if mykey == -1 794 continue 795 endif 796 797 " unmap both insert and visual modes 798 execute "silent! iunmap " . mykey . '<CR>' 799 execute "silent! vunmap " . mykey . '<CR>' 800 801 " drop from global 802 let g:CREAM_ADDON_MAPS{current + 1}_{1} = "" 803 let g:CREAM_ADDON_MAPS{current + 1}_{2} = "" 804 let g:CREAM_ADDON_MAPS{current + 1}_{3} = "" 805 806 elseif n == "map" 807 " go to unmap routine 808 let quit = Cream_addon_select() 809 810 elseif n == -1 811 " quit 812 let quit = 1 813 endif 814 815 endwhile 816 817 return 1 818 819endfunction 820 821" Cream_addon_unmap_dialog() {{{1 822function! s:Cream_addon_unmap_dialog(selections) 823" * Prompt user to select an addon through dialog 824" * Return key 825 826 let sav_go = &guioptions 827 set guioptions+=v 828 829 " remember focus 830 if !exists("s:focus_unmap") 831 let s:focus_unmap = 2 832 endif 833 834 let key = "" 835 let n = confirm( 836 \ a:selections . 837 \ "\n\n\n\n\---------------------------------------------------------------------------------------------------------------------------- " . 838 \ "\n", "&Up\n&Down\nU&nMap\n&Map...\n&Quit", s:focus_unmap, "Info") 839 if n == 1 840 let myreturn = "prev" 841 let s:focus_unmap = 1 842 elseif n == 2 843 let myreturn = "next" 844 let s:focus_unmap = 2 845 elseif n == 3 846 let myreturn = "unmap" 847 let s:focus_unmap = 2 848 elseif n == 4 849 let myreturn = "map" 850 let s:focus_unmap = 2 851 else 852 let myreturn = -1 853 let s:focus_unmap = 2 854 endif 855 856 " restore and return 857 let &guioptions = sav_go 858 return myreturn 859 860endfunction 861 862" Cream_addon_maps() {{{1 863function! s:Cream_addon_maps(key, icall, vcall) 864" map keys for add-ons 865" * return 1 if successful, -1 if either are bad 866 867 " both "<Nil>" 868 if a:icall ==? '<Nil>' && a:vcall ==? '<Nil>' 869 " do nothing 870 return 1 871 endif 872 873 " icall 874 if a:icall ==? '<Nil>' 875 " dead key 876 execute 'imap <silent> ' . a:key . ' <Nop>' 877 else 878 " validate prior to mapping 879 if s:Cream_addon_function_check(a:icall) == 1 880 execute 'imap <silent> ' . a:key . ' <C-b>:' . a:icall . '<CR>' 881 else 882 return -1 883 endif 884 endif 885 " vcall 886 if a:vcall ==? '<Nil>' 887 " dead key 888 execute 'vmap <silent> ' . a:key . ' <Nop>' 889 else 890 " validate function existance 891 if s:Cream_addon_function_check(a:vcall) == 1 892 " map 893 execute 'vmap <silent> ' . a:key . ' :' . a:vcall . '<CR>' 894 else 895 " unmap icall 896 execute 'iunmap <silent> ' . a:key . '<CR>' 897 return -1 898 endif 899 endif 900 901 " valid functions 902 return 1 903 904 905 "" if vcall passed 906 "if a:0 == 1 907 " let vcall = a:1 908 "endif 909 " 910 "" if visual call distinguished 911 "if exists("vcall") 912 " let vcall = a:1 913 " " if icall live 914 " if a:icall !=? '<Nil>' 915 " if s:Cream_addon_function_check(a:icall) == 1 916 " execute 'imap <silent> ' . a:key . ' <C-b>:' . a:icall . '<CR>' 917 " else 918 " " invalid 919 " return -1 920 " endif 921 " else 922 " " map key dead in insert mode 923 " execute 'imap <silent> ' . a:key . ' <Nop>' 924 " endif 925 " if s:Cream_addon_function_check(vcall) == 1 926 " execute 'vmap <silent> ' . a:key . ' :' . a:1 . '<CR>' 927 " else 928 " execute 'iunmap <silent> ' . a:key . '<CR>' 929 " " invalid (vcall) 930 " return -1 931 " endif 932 "else 933 " " map key dead in visual mode 934 " execute 'vmap <silent> ' . a:key . ' <Nop>' 935 " if s:Cream_addon_function_check(a:icall) == 1 936 " execute 'imap <silent> ' . a:key . ' <C-b>:' . a:icall . '<CR>' 937 " else 938 " " unmap vcall 939 " execute 'vunmap <silent> ' . a:key . '<CR>' 940 " " invalid 941 " return -1 942 " endif 943 "endif 944 " 945 "" valid functions 946 "return 1 947 948endfunction 949 950" Cream_addon_function_check() {{{1 951function! s:Cream_addon_function_check(call) 952" * verifies specified add-on call belongs to an existing function 953" * returns 1 on success, -1 on error 954" * warns that a particular function is not available and that mapping 955" will be skipped. 956 957 " empty returns success 958 if a:call == "" 959 return 1 960 endif 961 962 " strip preceding "*call " off function ("*" includes "<C-u>", et,al.) 963 let myfunction = substitute(a:call, '.*call\s*', '', 'g') 964 965 " test exists 966 if !exists("*" . myfunction) 967 call confirm( 968 \ "Warning: Referenced function \"" . myfunction . "\" not available to map. Skipping...\n" . 969 \ "\n", "&Ok", 1, "Info") 970 return -1 971 endif 972 return 1 973 974endfunction 975 976" Cream_addon_maps_init() {{{1 977function! Cream_addon_maps_init() 978" initialize a previous add-on map at session beginning 979" * add-on maps stored in global g:CREAM_ADDON_MAPS as array 980" * called by autocmd (thus, not s: script-local scope) 981 982 "if !exists("g:CREAM_ADDON_MAPS{0}") 983 " let g:CREAM_ADDON_MAPS{0} = 0 984 " return 985 "endif 986 987 " iterate through array, assigning maps 988 let i = 1 989 while i <= 8 990 " test exists and not empty 991 if !exists("g:CREAM_ADDON_MAPS{i}_{1}") 992 \ || g:CREAM_ADDON_MAPS{i}_{1} == "" 993 let i = i + 1 994 continue 995 endif 996 " map (validated here, too) 997 let n = s:Cream_addon_maps(g:CREAM_ADDON_MAPS{i}_{1}, g:CREAM_ADDON_MAPS{i}_{2}, g:CREAM_ADDON_MAPS{i}_{3}) 998 " if failed, remove from string 999 if n == -1 1000 unlet g:CREAM_ADDON_MAPS{i}_{1} 1001 unlet g:CREAM_ADDON_MAPS{i}_{2} 1002 unlet g:CREAM_ADDON_MAPS{i}_{3} 1003 " don't increment 1004 continue 1005 endif 1006 let i = i + 1 1007 endwhile 1008 1009endfunction 1010 1011" 1}}} 1012" vim:foldmethod=marker 1013