1" 2" cream-sort.vim -- Various sorting methods and algorithms 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" register as a Cream add-on {{{1 25if exists("$CREAM") 26 call Cream_addon_register( 27 \ 'Sort Selected Lines', 28 \ 'Sort the lines currently selected', 29 \ 'Sort the lines currently selected.', 30 \ 'Sort.Selected Lines', 31 \ '<Nil>', 32 \ 'BISort2' 33 \ ) 34 35 call Cream_addon_register( 36 \ 'Sort Selected Lines, Inverse', 37 \ 'Reverse sort the lines currently selected', 38 \ 'Reverse sort the lines currently selected.', 39 \ 'Sort.Selected Lines, Inverse', 40 \ '<Nil>', 41 \ "BISort2Reverse" 42 \ ) 43 44 call Cream_addon_register( 45 \ 'Invert Lines of Selection', 46 \ 'Invert the lines of the current selection', 47 \ 'Invert the lines of the current selection.', 48 \ 'Invert Selected Lines', 49 \ '<Nil>', 50 \ "Invert" 51 \ ) 52 53 call Cream_addon_register( 54 \ 'Uniq', 55 \ 'Eliminate duplicate, sorted lines', 56 \ 'Eliminate duplicate lines of an alphabetically sorted selection, simliar to the Unix utility uniq.', 57 \ 'Uniq Selected Lines', 58 \ '<Nil>', 59 \ '<C-u>call Cream_uniq("v")' 60 \ ) 61 62endif 63 64 65"********************************************************************* 66" BISort and related moved to library where we can access them 67" at startup. 68"********************************************************************* 69 70" File Sort (external sort) {{{1 71"--------------------------------------------------------------------- 72" Version: 1.5 73" Date: 2002 October 23 74" Source: http://vim.sourceforge.net/scripts/script.php?script_id=310 75" Author: Steve Hall [ digitect@mindspring.com ] 76" 77" Description: 78" Sort a file alphabetically by lines using external operating system 79" (Linux or Windows) commands which redirect the current file into 80" a sorted temporary file. 81" 82" Installation: 83" Simply copy this file and paste it into your vimrc. Or you can drop 84" the entire file into your plugins directory. 85" 86" Useage: 87" Use :call Cream_sortfile() to sort the current file alphabetically 88" by line. User is prompted to choose the column of the sort (default 89" is 1). 90" 91" Notes: 92" * The function uses the operating system's sort command (Windows or 93" Linux) for speed since our original use was for 7mb dictionary 94" wordlists(!). 95" 96" * Sorting is actually done in a temp file to preserve the original. 97" 98" * Please notice three other sort libraries included within the 99" script below the advertised first one. They were "research" during 100" the project and may serve certain users better than ours! 101" 102" ChangeLog: 103" 104" 2003-02-15 -- Version 1.5 105" * Unnamed buffer now saved as temporary file. 106" 107" 2002-10-23 -- Version 1.4 108" * Temp file overwrite option added. 109" 110" 2002-09-10 -- Version 1.3 111" * Quote path/filenames on Windows to handle spaces 112" 113" 2002-09-07 -- Version 1.2 114" * Added requirement that file be named (external redirection methods 115" require it) 116" * Added prompt to save modified file before proceeding 117" 118" 2002-07-18 -- Version 1.1 119" * Added temp file verification. 120" * Changed Linux "--key" to "-k" to work with some Linuxes. 121" * Eliminated Linux "-o=[FILENAME]" option to simple redirection to 122" work with some Linuxes. 123" 124" 2002-06-07 -- Version 1.0 125" * Initial Release 126 127" list as an add-on if Cream project in use 128if exists("$CREAM") 129 call Cream_addon_register( 130 \ 'Sort File', 131 \ 'Sort the current file by column', 132 \ 'Sort the current file alphabetically by line. User is prompted to choose the column of the sort (default is 1).', 133 \ 'Sort.File (external sort)', 134 \ 'call Cream_sortfile()' 135 \ ) 136endif 137 138function! Cream_sortfile() 139" sort a file's lines by column number 140" * option: sort selection by alternate column number (default 1) 141" * multi-platform: Windows 95-XP and Linux 142 143 " require file to be named (can't sort an un-named file due to the 144 " external redirections that this module uses) 145 if expand("%") == "" 146 "let n = confirm("File must be named before proceeding...", "&Ok\n&Cancel", 1, "Info") 147 "if n != 1 148 " call confirm("Canceled. Sort not performed.", "&Ok", 1, "Info") 149 " return 150 "endif 151 "if has("browse") 152 " browse confirm write 153 "else 154 " confirm write 155 "endif 156 "" if still unnamed, quit 157 "if expand("%") == "" 158 " call confirm( 159 " \ "Can not continue, document unnamed. Quitting...\n" . 160 " \ "\n", "&Ok", 1, "Info") 161 " return 162 "endif 163 let myfile = tempname() 164 call confirm( 165 \ "Saving as temp file " . myfile . " ...\n" . 166 \ "\n", "&Ok", 1, "Info") 167 execute "saveas " . myfile 168 endif 169 170 " prompt to save modified first 171 if &modified == 1 172 let n = confirm("Do you wish to save changes first?", "&Ok\n&No\n&Cancel", 1, "Info") 173 if n == 1 174 write 175 elseif n == 2 176 " carry on 177 else 178 call confirm("Canceled. Sort not performed.", "&Ok", 1, "Info") 179 return 180 endif 181 endif 182 183 " current file 184 let myfile = fnamemodify(expand("%"), ":p") 185 " escape backslashes (for Windows) 186 let myfile = escape(myfile, "\\") 187 " temp file (append extension .tmp) 188 let myfiletmp = myfile . ".tmp" 189 190 " verify temp file doesn't already exist 191 if filewritable(myfiletmp) == 1 192 let n = confirm("Filename \"" . myfiletmp . "\" already exists. Overwrite and continue?", "&Ok\n&Cancel", 2, "Warning") 193 if n != 1 194 return 195 endif 196 elseif filewritable(myfiletmp) == 2 197 call confirm("Unable to continue. A directory with the name \"" . myfiletmp . "\" already exists.", "&Ok", 1, "Warning") 198 return 199 endif 200 201 " get column to sort on 202 let mycol = inputdialog("Please enter column number to sort on (default 1)", "1") 203 if mycol == "" 204 return 205 endif 206 207 " validate 208 " convert to number 209 let mycol = mycol + 0 210 " make sure not empty 211 if mycol == 0 212 let mycol = "1" 213 endif 214 215 " sort (by platform command) 216 if has("win32") 217 218 " fix back slashes if any (I use :set shellslash) 219 let myfile = substitute(myfile,'/','\\','g') 220 let myfiletmp = substitute(myfiletmp,'/','\\','g') 221 222 " Windows 223 "---------------------------------------------------------------------- 224 " SORT /? 225 " 226 " Sorts input and writes results to the screen, a file, or another device 227 " 228 " SORT [/R] [/+n] [[drive1:][path1]filename1] [> [drive2:][path2]filename2] 229 " [command |] SORT [/R] [/+n] [> [drive2:][path2]filename2] 230 " 231 " /R Reverses the sort order; that is, sorts Z to A, 232 " then 9 to 0. 233 " /+n Sorts the file according to characters in 234 " column n. 235 " [drive1:][path1]filename1 Specifies file(s) to be sorted 236 " [drive2:][path2]filename2 Specifies a file where the sorted input is to be 237 " stored. 238 " command Specifies a command whose output is to be sorted. 239 240 " command (extra quotes handle file/pathnames with spaces) 241 execute ":silent !SORT /+" . mycol . " " . "\"" . myfile . "\"" . " > " . "\"" . myfiletmp . "\"" 242 243 elseif has("unix") 244 245 " Linux | Cygwin 246 "---------------------------------------------------------------------- 247 " sort --help 248 " 249 " Usage: sort [OPTION]... [FILE]... 250 " Write sorted concatenation of all FILE(s) to standard output. 251 " 252 " Ordering options: 253 " 254 " Mandatory arguments to long options are mandatory for short options too. 255 " -b, --ignore-leading-blanks ignore leading blanks 256 " -d, --dictionary-order consider only blanks and alphanumeric characters 257 " -f, --ignore-case fold lower case to upper case characters 258 " -g, --general-numeric-sort compare according to general numerical value 259 " -i, --ignore-nonprinting consider only printable characters 260 " -M, --month-sort compare (unknown) < `JAN' < ... < `DEC' 261 " -n, --numeric-sort compare according to string numerical value 262 " -r, --reverse reverse the result of comparisons 263 " 264 " Other options: 265 " 266 " -c, --check check whether input is sorted; do not sort 267 " -k, --key=POS1[,POS2] start a key at POS1, end it at POS 2 (origin 1) 268 " -m, --merge merge already sorted files; do not sort 269 " -o, --output=FILE write result to FILE instead of standard output 270 " -s, --stable stabilize sort by disabling last-resort comparison 271 " -S, --buffer-size=SIZE use SIZE for main memory buffer 272 " -t, --field-separator=SEP use SEP instead of non- to whitespace transition 273 " -T, --temporary-directory=DIR use DIR for temporaries, not $TMPDIR or /tmp 274 " multiple options specify multiple directories 275 " -u, --unique with -c: check for strict ordering 276 " otherwise: output only the first of an equal run 277 " -z, --zero-terminated end lines with 0 byte, not newline 278 " --help display this help and exit 279 " --version output version information and exit 280 " 281 " POS is F[.C][OPTS], where F is the field number and C the character position 282 " in the field. OPTS is one or more single-letter ordering options, which 283 " override global ordering options for that key. If no key is given, use the 284 " entire line as the key. 285 " 286 " SIZE may be followed by the following multiplicative suffixes: 287 " % 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y. 288 " 289 " With no FILE, or when FILE is -, read standard input. 290 " 291 " *** WARNING *** 292 " The locale specified by the environment affects sort order. 293 " Set LC_ALL=C to get the traditional sort order that uses 294 " native byte values. 295 " 296 " Report bugs to <bug-textutils@gnu.org>. 297 298 " command 299 " * original: 300 " execute ":!sort --key=" . mycol . " -fid -o=" . myfiletmp . " " . myfile 301 " * some systems cannot use the "--key=" option, use "-k" 302 " * some systems cannot use the "-o=" option, use redirection 303 execute ":!sort -k " . mycol . " -fid " . myfile . " > " . myfiletmp 304 305 else 306 call confirm("Sorry! This platform untested. \n\nPlease contact us at\n http://cream.sourceforge.net ", "&Ok", 1, "Info") 307 return 308 endif 309 310 " edit temp file 311 execute ":edit " . myfiletmp 312 313 " warn we're in temp file 314 call confirm("Now in temp file:\n " . myfiletmp . "\n\nPreserved original file:\n " . myfile . "\n\n", "&Ok", 1, "Warning") 315 316endfunction 317" 1}}} 318" Various other (unused) sort functions 319" Line Sort {{{1 320 321"" list as an add-on if Cream project in use 322"if exists("$CREAM") 323" call Cream_addon_register( 324" \ 'Sort Selection', 325" \ 'Sort the current selection', 326" \ 'Sort the current selection.', 327" \ 'Sort.Selection', 328" \ '<Nil>', 329" \ 'Sort' 330" \ ) 331 332" call Cream_addon_register( 333" \ 'Sort Selection, Inverse', 334" \ 'Reverse sort the current selection', 335" \ 'Reverse sort the current selection.', 336" \ 'Sort.Selection, Inverse', 337" \ '<Nil>', 338" \ 'SortReverse' 339" \ ) 340"endif 341 342" * Extended version found in :help eval-examples 343" * See also in explorer.vim 344" * Extremely slow (>10 min.) for large files (dictionaries) 345" 346" Author: Robert Webb <RobertW at beam.com.au> 347" 348 349" Function for use with Sort(), to compare two strings. 350func! <SID>Strcmp(str1, str2) 351 if (a:str1 < a:str2) 352 return -1 353 elseif (a:str1 > a:str2) 354 return 1 355 else 356 return 0 357 endif 358endfunction 359" Function for use with Sort(), to compare two strings. 360func! <SID>Stricmp(str1, str2) 361 let st1=substitute(a:str1,'^.*$', '\L&', "") 362 let st2=substitute(a:str1,'^.*$', '\L&', "") 363 if (st1 < st2) 364 return -1 365 elseif (st1 > st2) 366 return 1 367 else 368 return 0 369 endif 370endfunction 371 372" Sort lines. SortR() is called recursively. 373func! <SID>SortR(start, end, cmp) 374 if (a:start >= a:end) 375 return 376 endif 377 let partition = a:start - 1 378 let middle = partition 379 let partStr = getline((a:start + a:end) / 2) 380 let i = a:start 381 while (i <= a:end) 382 let str = getline(i) 383 exec "let result = " . a:cmp . "(str, partStr)" 384 if (result <= 0) 385 " Need to put it before the partition. Swap lines i and partition. 386 let partition = partition + 1 387 if (result == 0) 388 let middle = partition 389 endif 390 if (i != partition) 391 let str2 = getline(partition) 392 call setline(i, str2) 393 call setline(partition, str) 394 endif 395 endif 396 let i = i + 1 397 endwhile 398 399 " Now we have a pointer to the "middle" element, as far as partitioning 400 " goes, which could be anywhere before the partition. Make sure it is at 401 " the end of the partition. 402 if (middle != partition) 403 let str = getline(middle) 404 let str2 = getline(partition) 405 call setline(middle, str2) 406 call setline(partition, str) 407 endif 408 call <SID>SortR(a:start, partition - 1, a:cmp) 409 call <SID>SortR(partition + 1, a:end, a:cmp) 410endfunc 411 412" To Sort a range of lines, pass the range to Sort() along with the name of a 413" function that will compare two lines. 414func! <SID>Sort(cmp) range 415 call <SID>SortR(a:firstline, a:lastline, a:cmp) 416endfunc 417 418" :Sort takes a range of lines and sorts them. 419command! -nargs=0 -range Sort <line1>,<line2>call <SID>Sort('<SID>Strcmp') 420command! -nargs=0 -range ISort <line1>,<line2>call <SID>Sort('<SID>Stricmp') 421 422"+++ Cream: 423command! -nargs=0 -range SortReverse <line1>,<line2>call <SID>Sort('<SID>StrcmpR') 424func! <SID>StrcmpR(str2, str1) 425 if (a:str1 < a:str2) 426 return -1 427 elseif (a:str1 > a:str2) 428 return 1 429 else 430 return 0 431 endif 432endfunction 433"+++ 434 435" Line Sort (BISort) {{{1 436" Source: Piet Delport, vim@vim.org list, 2003-05-01 437 438"" list as an add-on if Cream project in use 439"if exists("$CREAM") 440" call Cream_addon_register( 441" \ 'Sort Selection', 442" \ 'Sort the current selection', 443" \ 'Sort the current selection.', 444" \ 'Sort.Selection (BISort)', 445" \ '<Nil>', 446" \ 'call Cream_BISort_call("v")' 447" \ ) 448" 449" call Cream_addon_register( 450" \ 'Sort Selection, Inverse', 451" \ 'Reverse sort the current selection', 452" \ 'Reverse sort the current selection.', 453" \ 'Sort.Selection, Inverse (BISort)', 454" \ '<Nil>', 455" \ 'call Cream_BISort_call_reverse("v")' 456" \ ) 457"endif 458 459function! Cream_BISort_call(mode) 460" calls BISort2, establishing the range 461 if a:mode != "v" 462 return 463 endif 464 normal gv 465 call BISort(line("'<"), line("'>"), '<SID>Strcmp') 466endfunction 467function! Cream_BISort_call_reverse(mode) 468" calls BISort2, establishing the range 469 if a:mode != "v" 470 return 471 endif 472 normal gv 473 call BISort(line("'>"), line("'<"), '<SID>StrcmpR') 474endfunction 475 476function! BISort(start, end, cmp) 477 let compare_ival_mid = 'let diff = '.a:cmp.'(i_val, getline(mid))' 478 let i = a:start + 1 479 while i <= a:end 480 " find insertion point via binary search 481 let i_val = getline(i) 482 let lo = a:start 483 let hi = i 484 while lo < hi 485 let mid = (lo + hi) / 2 486 exec compare_ival_mid 487 if diff < 0 488 let hi = mid 489 else 490 let lo = mid + 1 491 if diff == 0 | break | endif 492 endif 493 endwhile 494 " do insert 495 if lo < i 496 exec i.'d_' 497 call append(lo - 1, i_val) 498 endif 499 let i = i + 1 500 endwhile 501endfunction 502 503" Line length sorting {{{1 504" 505" See: genutils.vim by Hari Krishna Dara <hari_vim at yahoo.com> 506" http://vim.sourceforge.net/scripts/script.php?script_id=197 507" 508 509 510" Perl file sorting {{{1 511" by Colin Keith, on the vim@vim.org mailing list 03 Jun 2002 512" * Uses Perl 513function! VimPerlSort(...) range 514 if !has('perl') 515 echoerr "Perl not installed, unable to continue" 516 return 517 endif 518 519 let s:col = 1 520 let s:order = 0 521 522 " Calculate sort method 523 if exists('a:1') && a:1 > 0 && a:1 < 5 524 let s:order = a:1 525 526 " And if we want any column other than the first 527 if exists('a:2') 528 let s:col = a:2 529 endif 530 endif 531 532 " alphabetical 533 if s:order == 1 534 let s:sort = 'my $x = substr($a, '. s:col. ', 1)||""; '. 535 \ 'my $y = substr($b, '. s:col. ', 1)||""; '. 536 \ 'return $x cmp $y;' 537 " reverse alphabetical 538 elseif s:order == 2 539 let s:sort = 'my $x = substr($a, '. s:col. ', 1)||""; '. 540 \ 'my $y = substr($b, '. s:col. ', 1)||""; '. 541 \ 'return $y cmp $x;' 542 " numerical 543 elseif s:order == 3 544 let s:sort = 'my $x = substr($a, '. s:col. ', 1)||0; '. 545 \ 'my $y = substr($b, '. s:col. ', 1)||0; '. 546 \ 'return $x <=> $y;' 547 " reverse numerical 548 elseif s:order == 4 549 let s:sort = 'my $x = substr($a, '. s:col. ', 1)||0; '. 550 \ 'my $y = substr($b, '. s:col. ', 1)||0; '. 551 \ 'return $y <=> $x;' 552 else 553 let s:sort = '$a cmp $b' 554 endif 555 556 " Load into memory 557 execute ':perl @data = sort { '. s:sort . ' } '. 558 \ '$curbuf->Get('. a:firstline. '..'. a:lastline. ')' 559 execute ':perl $curbuf->Set('. a:firstline. ', @data)' 560 561 " just to make sure the memory is released: 562 :perl @data=() 563 564endfunction 565" 1}}} 566" vim:foldmethod=marker 567