1" 2" cream-columns.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 3 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" Updated: 2006-05-13 14:32:50EDT 25" Source: (excerpted from the Cream project) 26" Author: Steve Hall [ digitect@mindspring.com ] 27" 28" Description: 29" Intuitively type, delete and backspace in a vertical column. 30" 31" One of the many custom utilities and functions for gVim from the 32" Cream project ( http://cream.sourceforge.net ), a configuration of 33" Vim for those of us familiar with Apple or Windows software. 34" 35" Installation: 36" 37" Known Issues: 38" * Undo doesn't always track position correctly 39" 40" Notes: 41" * redraw! is required for any key that changes the background 42" (color). Unfortunately this includes shifted-motion keys, the very 43" keys we'd like to avoid having to clear and redraw the screen 44" after due to speed concerns. 45 46function! Cream_columns() 47" setup stuff before calling main motion/insert stuff 48 49 " Hack: We can select the first column only when list is on. So we 50 " provide ourselves two sets of &listchars: 51 " On: Cream_listchars_init() 52 " Off: Cream_listchars_col_init() 53 54 " make invisible (use execute to maintain trailing char!) 55 if &list == 0 56 call Cream_listchars_col_init() 57 " MUST proceed initial Visual-block mode start 58 set list 59 " save "column &list" setting (we won't touch global) 60 let g:cream_list_col = 0 61 else 62 let g:cream_list_col = 1 63 endif 64 65 " position (stupid work around for <C-b> stuff at first column over tab) 66 let mycol = virtcol('.') 67 68 " start visual block mode 69 execute "normal \<C-v>" 70 " show initial position 71 redraw! 72 73 " set virtual column behavior 74 " save existing state 75 let myvirtualedit = &virtualedit 76 " all motions/bs/del are always one character 77 set virtualedit=all 78 79 " motion/bs/del space according to characters selected 80 "*** TODO: Broken: 81 " * flakey on Backspace 82 " * flakey when selecting multiple lines preceded with mixed tabs 83 " and spaces 84 "set virtualedit= 85 "*** 86 87 "*** TODO: broken--makes window pos jump 88 "" save screen pos 89 "" HACK: set guifont=* moves screen on WinXP 90 "call Cream_screen_get() 91 " 92 "" change to column-specific font 93 "call Cream_fontset_columns() 94 " 95 "" restore screen 96 "" HACK: set guifont=* moves screen on WinXP 97 "call Cream_screen_init() 98 "*** 99 100 " avoid the flash 101 " save existing state 102 let myerrorbells = &errorbells 103 set noerrorbells 104 105 " clear popup menu 106 silent! unmenu PopUp 107 silent! unmenu! PopUp 108 109 " main call 110 call Cream_column_mode() 111 112 " reset mouse menu 113 call Cream_menu_popup() 114 115 "*** TODO: broken 116 "" restore font 117 "call Cream_font_init() 118 "" restore screen 119 "" HACK: set guifont=* moves screen on WinXP 120 "call Cream_screen_init() 121 "*** 122 123 " restore listchars 124 call Cream_listchars_init() 125 " restore list state 126 " Note: The user may have toggled on/off during col mode. We'll 127 " restore global settings based on last col state. 128 if g:cream_list_col == 1 129 set list 130 let g:LIST = 1 131 elseif g:cream_list_col == 0 132 set nolist 133 let g:LIST = 0 134 endif 135 unlet g:cream_list_col 136 " restore virtualedit 137 let &virtualedit = myvirtualedit 138 " restore errorbells 139 let &errorbells = myerrorbells 140 141endfunction 142 143function! Cream_listchars_col_init() 144 execute 'set listchars=eol:\ ,tab:\ \ ,trail:\ ,extends:\ ,precedes:\ ' 145endfunction 146 147function! Cream_column_mode() 148" Motion, insert and delete stuff (visual block mode has already been 149" entered) 150 151 " total loops 152 let i = 0 153 let j = 0 154 let s:lastevent = "nothing" 155 while i >= 0 156 157 let i = i + 1 158 159 " get character 160 let mychar = getchar() 161 162""*** DEBUG: 163"let n = confirm( 164" \ "DEBUG:\n" . 165" \ " mychar = \"" . mychar . "\"\n" . 166" \ " char2nr(mychar) = \"" . char2nr(mychar) . "\"\n" . 167" \ " nr2char(mychar) = \"" . nr2char(mychar) . "\"\n" . 168" \ " strlen(mychar) = \"" . strlen(mychar) . "\"\n" . 169" \ "\n", "&Ok\n&Cancel", 1, "Info") 170"if n != 1 171" return 172"endif 173""*** 174 175 if exists("eattest") 176 " <C-u> 177 if mychar == 21 178 " eat the rest 179 call s:Cream_column_eatchar() 180 normal gv 181 182 " we can't yet recover from this error 183 call s:Cream_column_backspace() 184 " unselect (from select mode) 185 normal vv 186 redraw! 187 break 188 endif 189 endif 190 191 " ":" (loop if precursor to <C-u>) 192 if mychar == 58 193 let eattest = ":" 194 call s:Cream_column_insert(mychar) 195 endif 196 197 " test character for motion/selection 198 " <Esc> quit 199 if mychar == 27 200 execute "normal " . mychar 201 redraw! 202 break 203 204 " <Undo> (Ctrl+Z) 205 elseif mychar == 26 206 \|| mychar == "\<Undo>" 207 execute "normal \<Esc>" 208 normal u 209 normal gv 210 " backup a column to maintain insertion positions 211 if s:lastevent == "insert" 212 normal h 213 normal o 214 normal h 215 elseif s:lastevent == "backspace" 216 normal l 217 normal o 218 normal l 219 endif 220 221 redraw! 222 223 " <C-c> (Copy) 224 elseif mychar == 3 225 \ || mychar == "\<C-c>" 226 \ || nr2char(mychar) == "" 227 execute "normal \"+y<CR>" 228 normal gv 229 redraw! 230 call confirm( 231 \ "To paste this selection in columns, re-enter Column Mode first (Edit > Column Select).\n" . 232 \ "\n", "&Ok", 1, "Info") 233 234 " (alpha-numeric) or <Tab>, but not colon ":" 235 elseif mychar > 31 236 \&& mychar < 127 237 \&& mychar != 58 238 \|| mychar == 9 239 240 call s:Cream_column_insert(mychar) 241 242 " <CR> (same as delete) 243 elseif mychar == 13 244 normal x 245 break 246 247 " <C-b> and <C-u> (menu calls) 248 elseif mychar == 15 249 \ || nr2char(mychar) == "" 250 251 " eat chars until <CR> 252 call s:Cream_column_eatchar() 253 254 " <Del> 255 elseif char2nr(mychar[0]) == 128 256 \&& char2nr(mychar[1]) == 107 257 \&& char2nr(mychar[2]) == 68 258 " Note: 259 " 260 " elseif mychar == "\<Del>" 261 " \ || mychar == 127 262 " 263 " The above fails, apparently due to case-sensitivity with 264 " <Down> ("€kd") conflicting with <Del> ("€kD"). 265 266 " delete 267 normal x 268 normal gv 269 270 " retract selection width to a single column 271 let mycol = virtcol(".") 272 normal o 273 if virtcol(".") < mycol 274 " negate left-to-right v. right-to-left selection 275 " discrepancies 276 let mycol = virtcol(".") 277 normal o 278 endif 279 while virtcol(".") > mycol 280 normal h 281 endwhile 282 283 let s:lastevent = "delete" 284 285 redraw! 286 287 " toggle &list 288 elseif mychar == "\<F4>" 289 " Note: List must stay on during column mode to 290 " work around some Vim bugs. We'll just toggle 291 " whether the characters show or not based on the 292 " two sets of listchars we set at the top. 293 294 if g:cream_list_col == 0 295 call Cream_listchars_init() 296 let g:cream_list_col = 1 297 let g:LIST = 1 298 else 299 call Cream_listchars_col_init() 300 let g:cream_list_col = 0 301 let g:LIST = 0 302 endif 303 redraw! 304 305 elseif mychar == "\<Down>" 306 \|| mychar == "\<Up>" 307 \|| mychar == "\<PageUp>" 308 \|| mychar == "\<PageDown>" 309 " bail 310 execute "normal " . mychar 311 break 312 313 elseif mychar == "\<Left>" 314 \|| mychar == "\<Right>" 315 \|| mychar == "\<Home>" 316 \|| mychar == "\<End>" 317 " bail 318 execute "normal " . mychar 319 break 320 321 elseif mychar == "\<BS>" 322 323 call s:Cream_column_backspace() 324 let s:lastevent = "backspace" 325 326 elseif mychar == "\<S-Left>" 327 \|| mychar == "\<S-Right>" 328 \|| mychar == "\<S-Home>" 329 \|| mychar == "\<S-End>" 330 331 execute "normal " . mychar 332 let s:lastevent = "motion" 333 redraw! 334 335 elseif mychar == "\<LeftMouse>" 336 \|| char2nr(mychar[0]) == 128 337 \&& char2nr(mychar[1]) == 253 338 \&& char2nr(mychar[2]) == 44 339 \&& char2nr(mychar[3]) == 0 340 execute "normal " . mychar 341 342 redraw! 343 break 344 345 """elseif mychar == "\<RightMouse>" 346 """ \|| char2nr(mychar[0]) == 128 347 """ \&& char2nr(mychar[1]) == 253 348 """ \&& char2nr(mychar[2]) == 50 349 """ \&& char2nr(mychar[3]) == 0 350 """ \|| mychar == "\<MiddleMouse>" 351 """ \|| char2nr(mychar[0]) == 128 352 """ \&& char2nr(mychar[1]) == 253 353 """ \&& char2nr(mychar[2]) == 47 354 """ \&& char2nr(mychar[3]) == 0 355 """ 356 """ call confirm( 357 """ \ "Right and Middle mouse buttons are not functional in column mode.\n" . 358 """ \ "\n", "&Ok", 1, "Info") 359 """ redraw! 360 """ 361 """elseif mychar == "\<S-RightMouse>" 362 """ \|| mychar == "\<M-S-RightMouse>" 363 """ 364 """ call confirm( 365 """ \ "Sorry, S-RightMouse not handled in column mode yet!\n" . 366 \ "\n", "&Ok", 1, "Info") 367 368 " <C-x> (Cut) 369 elseif char2nr(mychar[0]) == 50 370 \&& char2nr(mychar[1]) == 52 371 execute "normal \"+x<CR>" 372 373 redraw! 374 375 call confirm( 376 \ "To paste this selection in columns, re-enter Column Mode first (Edit > Column Select).\n" . 377 \ "\n", "&Ok", 1, "Info") 378 379 break 380 381 " <C-p> (Paste) 382 elseif char2nr(mychar[0]) == 50 383 \&& char2nr(mychar[1]) == 50 384 execute "normal \"+p<CR>" 385 386 redraw! 387 break 388 389 " other key (untrapped) 390 else 391 392 " just execute that key 393 execute "normal " . mychar 394 " forget backspace condition 395 if exists("s:stop") 396 unlet s:stop 397 endif 398 399 redraw! 400 401 endif 402 403 endwhile 404 405 redraw! 406 407endfunction 408 409function! s:Cream_column_eatchar() 410" eat input until a <CR> is reached 411 412 " loop if menu called (on <C-b> discovered below) 413 while !exists("finished") 414 let mychar = getchar() 415 " until <CR> 416 if mychar == 13 417 let finished = 1 418 call confirm( 419 \ "Sorry, menu items are not yet available during column mode.\n" . 420 \ "\n", "&Ok", 1, "Info") 421 endif 422 endwhile 423 424endfunction 425 426function! s:Cream_column_insert(char) 427 428 " retract selection width to a single column 429 let mycol = virtcol(".") 430 normal o 431 if virtcol(".") < mycol 432 " negate left-to-right v. right-to-left selection 433 " discrepancies 434 let mycol = virtcol(".") 435 normal o 436 endif 437 while virtcol(".") > mycol 438 normal h 439 endwhile 440 441 " forget backspace condition 442 if exists("s:stop") 443 unlet s:stop 444 endif 445 446 " insert character 447 execute "normal I" . nr2char(a:char) 448 " reselect and recover position 449 normal gv 450 normal l 451 normal o 452 normal l 453 "normal o 454 455 let s:lastevent = "insert" 456 457 redraw! 458 459endfunction 460 461function! s:Cream_column_backspace() 462 463 " at non-first columns, move left a column 464 if virtcol(".") != 1 465 normal h 466 normal o 467 normal h 468 normal o 469 endif 470 471 " delete selection 472 normal x 473 " reselect 474 normal gv 475 476 " retract selection width to a single column 477 let mycol = virtcol(".") 478 normal o 479 if virtcol(".") < mycol 480 " negate left-to-right v. right-to-left selection 481 " discrepancies 482 let mycol = virtcol(".") 483 normal o 484 endif 485 while virtcol(".") > mycol 486 normal h 487 endwhile 488 redraw! 489 490endfunction 491 492" column mode font set/change 493function! Cream_fontset_columns() 494 let myos = Cream_getoscode() 495 if exists("g:CREAM_FONT_COLUMNS_{myos}") 496 " had a problem with guifont being set to * in an error. 497 if g:CREAM_FONT_COLUMNS_{myos} != "*" 498 let &guifont = g:CREAM_FONT_COLUMNS_{myos} 499 endif 500 else 501 " Don't prompt to initialize font... use default. 502 "call Cream_font_init() 503 endif 504endfunction 505 506function! Cream_fontinit_columns() 507 let mymsg = "" . 508 \ "This font setting is specifically for column mode. We highly\n" . 509 \ "recommend that you choose the same font as your regular font\n" . 510 \ "with only a style variation such as \"italic.\"" 511 if has("gui_running") && has("dialog_gui") 512 call confirm(mymsg, "&Ok", 1, "Info") 513 else 514 echo mymsg 515 endif 516 517 let myos = Cream_getoscode() 518 519 " call dialog... 520 set guifont=* 521 " if still empty or a "*", user may have cancelled; do nothing 522 if &guifont == "" && g:CREAM_FONT_COLUMNS_{myos} != "" || 523 \ &guifont == "*" && g:CREAM_FONT_COLUMNS_{myos} != "" 524 " do nothing 525 else 526 let g:CREAM_FONT_COLUMNS_{myos} = &guifont 527 endif 528 " revert to main font if we're not in column mode 529 let mymode = mode() 530 if mymode != "" 531 if exists("g:CREAM_FONT_{myos}") 532 let &guifont = g:CREAM_FONT_{myos} 533 endif 534 endif 535endfunction 536 537