├── README.asciidoc ├── examples ├── find_lines_containing_larger_numbers.vim ├── fix_bad_html_table_scrape.vim └── sum_green_lines.vim ├── plugin └── stack.vim └── test ├── 001_maths.vim ├── 002_strings.vim ├── 003_lists.vim ├── README └── _setup.vim /README.asciidoc: -------------------------------------------------------------------------------- 1 | Vim Stack 2 | --------- 3 | 4 | __This is not forth for Vim.__ 5 | 6 | VimStack brings stack processing to your Vim command-line. 7 | 8 | Ever wanted to sum the first number on every line containing the word "green"? 9 | 10 | :echo _S.push(GCollect('green')).matchstr('\d\+').sumall().top() 11 | 12 | VimStack provides a global stack called ++_S++ by default, but you're free to 13 | create as many as you like. 14 | 15 | There are many functions for manipulating the stack and operating on the 16 | current value at the top of the stack (TOS). See ++:help vim-stack-functions++ 17 | for a complete list. Here is a quick overview: 18 | 19 | VimStack Function Overview 20 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 | 22 | .... 23 | TOS = the Top Of Stack element 24 | obj = an element or a list of elements 25 | e = an element 26 | e' = a modified element 27 | n, m = integer counts 28 | = a function argument 29 | <[str]> = an optional function argument 30 | [s] = whole stack 31 | [e] = list of elements 32 | [e'] = modified list of elements 33 | -- = chainable stack return 34 | -> = returns a non-stack value (non-chainable) 35 | 36 | flush ( [s] -- [] ) - delete the entire stack 37 | empty ( [s] -> bool ) - true if stack is empty 38 | size ( [s] -> int ) - number of elements on the stack 39 | pushsize ( e1..en -- e1..en n ) - push number of elements onto the stack 40 | 41 | map ( [e] -- [e'] ) - map over list at TOS 42 | nmap ( e1..en -- e1'..en' ) - map over elements 43 | mapall ( [s] -- [s'] ) - map over whole stack 44 | filter ( [e] -- [e'] ) - filter list at TOS through 45 | nfilter ( e1..en -- ei'..ej' ) - filter elements through 46 | filterall ( [s] -- [s'] ) - filter whole stack through 47 | sort ( [s] <[func]> -- [s'] ) - sort whole stack (opt. sort ) 48 | reverse ( [e] -- [e'] ) - reverse list at TOS 49 | ( e1..en -- en..e1 ) - reverse elements 50 | range ( [s] -- [s + [en..em]] ) - push a list containing range .. 51 | rangeeach ( [s] -- [s + en..em] ) - push each element of range .. 52 | 53 | push ( [s] -- [s + obj] ) - push element onto stack 54 | pusheach ( [s] [] -- [s + e1..en] ) - push each element of list onto stack 55 | pop ( [s] -> TOS ) - remove and return TOS 56 | ( [s] -> [e1..en] ) - remove and return elements from stack 57 | top ( [s] -> [s] ) - return TOS 58 | 59 | swap ( e1 e2 -- e2 e1 ) - swap the top two elements 60 | over ( e1 e2 -- e1 e2 e1 ) - copy second element to TOS 61 | rot ( e1 e2 e3 -- e2 e3 e1 ) - single element, 3 span left rotate 62 | rotate ( [s] -- [s'] ) - element, span right rotate 63 | drop ( [s] -- [s'] ) - silently remove TOS 64 | dup ( e -- e e ) - duplicate TOS 65 | keep ( [s] -- e ) - drop all but TOS 66 | ( [s + e1..en] -- e1..en ) - drop all but elements 67 | sink ( [s + e] -- [e + s] ) - move TOS to bottom of stack 68 | ( [s + t + e] -- [s + e + t] ) - move TOS to from bottom of stack 69 | ( [s + t + e] <-m> -- [s + e + t] ) - move TOS down elements 70 | 71 | p ( [s] -- [s] ) - print whole stack 72 | ( [s] -- [s] ) - print elements of stack 73 | s ( [s] -> [s] ) - returns a copy of the stack 74 | string ( [s] -> "[s]" ) - returns a string() copy of the stack 75 | 76 | add ( e1 e2 -- e1+e2 ) - add top two numbers 77 | sub ( e1 e2 -- e1-e2 ) - subtract top two numbers 78 | mul ( e1 e2 -- e1*e2 ) - multiply top two numbers 79 | div ( e1 e2 -- e1/e2 ) - divide top two numbers 80 | fdiv ( e1 e2 -- e1/e2 ) - divide (float) top two numbers 81 | mod ( e1 e2 -- e1%e2 ) - modulo top two numbers 82 | sum ( [e1..en] -- [e1+e2+...+en] ) - replace TOS with sum of its elements 83 | nsum ( [s] -- [s] + e ) - replace elements with their sum 84 | sumall ( [s] -- e ) - replace whole stack with its sum 85 | 86 | join ( e1 e2 <[sep='']> -- "e1e2" ) - join top two elements with 87 | njoin ( e1..en -- "e1e2>..en" ) - join elems 88 | joinall ( [s] -- "e1..en" ) 89 | len ( e -- e' ) - replace TOS with its length 90 | matchstr ( e -- e' ) - replace TOS with string matches 91 | ( [e] -- [e'] ) - replace elements of TOS with str matches 92 | matchlist ( e -- [e'] ) - replace TOS with match list 93 | ( [e] -- [[e']] ) - replace each element of TOS with match list 94 | substitute ( e -- e' ) - replace TOS with substitution result 95 | ( [e] -- [e'] ) - replace elements of TOS with substitutions 96 | 97 | list ( [s] -- [s'] ) - wrap elements at TOS in a list 98 | split ( "e" -- e1 .. en ) - split string at TOS on 99 | explode ( [e] -- e1 .. en ) - replace list at TOS with its elements 100 | zip ( [e1] [e2] -- [e1a,e2a,e1b,e2b] ) - combine two lists into one 101 | ( [e1] [e2] -- [[e1a,e2a], [e1b,e2b]] ) - combine two lists into one of pairs 102 | ( [e1] [e2] -- ['e1a:e2a', 'e1b:e2b'] ) - combine two lists into one of connected strings separated by 103 | zipset ( e1 e2 -- [e1a,e2a,e1b,e2b] ) - combine <2*n> elements into a list 104 | slice ( [e] -- [e1..en/2] [e(n/2)+1..en] ) - split a list into slices of size 105 | 106 | setline ( e -- ) - remove TOS and write it to buffer at 107 | append ( e -- ) - remove TOS and write it to buffer after 108 | .... 109 | 110 | Installation 111 | ~~~~~~~~~~~~ 112 | 113 | If you don't have a preferred installation method, I recommend installing 114 | https://github.com/tpope/vim-pathogen[pathogen.vim], and then simply copy and 115 | paste: 116 | 117 | cd ~/.vim/bundle 118 | git clone git://github.com/dahu/vim-stack.git 119 | 120 | Once help tags have been generated, you can view the manual with ++:help 121 | VimStack++. 122 | 123 | Contribute 124 | ~~~~~~~~~~ 125 | 126 | - Issue Tracker: https://github.com/dahu/vim-stack/issues 127 | - Source Code: https://github.com/dahu/vim-stack 128 | 129 | FAQ 130 | ~~~ 131 | 132 | * Why?! 133 | + 134 | VimStack came about from an interest in the stack-processing model of forth-ish 135 | languages, and a desire to reduce the number of steps needed at the command-line 136 | to get certain tasks done in Vim. 137 | 138 | Support 139 | ~~~~~~~ 140 | 141 | If you are having issues, please let me know. 142 | I'm contactable by email and frequently join the #vim channel on freenode IRC. 143 | 144 | * Barry Arthur bairui 145 | 146 | License 147 | ~~~~~~~ 148 | 149 | VimStack is Copyright (c) Barry Arthur. Distributed under the same terms as Vim 150 | itself. See ++:help license++ for details. 151 | -------------------------------------------------------------------------------- /examples/find_lines_containing_larger_numbers.vim: -------------------------------------------------------------------------------- 1 | " Shows the lines containing numbers bigger than 50 (with line numbers) 2 | 3 | function! Run(n) 4 | call g:_S.flush() 5 | call g:_S.pushline(1,'$').enumerate() 6 | \.filter('v:val[1] =~ "\\d"') 7 | \.unzip().extract('\d\+').zip(1) 8 | \.filter2('v:val > 50') 9 | \.unzip().map('join(v:val, ", ")').zip(": ") 10 | call append('$', g:_S.top()) 11 | endfunction 12 | 13 | call Run(search('^\s*fini\%[sh]', 'n')+1) 14 | finish 15 | blah 10 16 | 20 blah 17 | blah 30 blah 99 18 | blah 40 19 | 50 blah 20 | 21 | blah 60 blah 22 | blah 70 23 | 80 blah 24 | blah 90 blah 100 blah 25 | -------------------------------------------------------------------------------- /examples/fix_bad_html_table_scrape.vim: -------------------------------------------------------------------------------- 1 | function! Run(n) 2 | call g:_S.flush() 3 | call g:_S.push(getline(a:n,'$')).slice(5) 4 | %delete 5 | call append(0, map(g:_S.s(), 'join(v:val, "\t")')) 6 | endfunction 7 | 8 | call Run(search('^\s*fini\%[sh]', 'n')+1) 9 | finish 10 | one 11 | two 12 | three 13 | four 14 | five 15 | a 16 | b 17 | c 18 | d 19 | e 20 | 1 21 | 2 22 | 3 23 | 4 24 | 5 25 | -------------------------------------------------------------------------------- /examples/sum_green_lines.vim: -------------------------------------------------------------------------------- 1 | " Sums together the numbers of only the lines containing 'green' 2 | 3 | function! Run(n) 4 | call g:_S.flush() 5 | call g:_S.push(GCollect('green')).matchstr('\d\+').sum() 6 | call append('$', g:_S.top()) 7 | endfunction 8 | 9 | call Run(search('^\s*fini\%[sh]', 'n')+1) 10 | finish 11 | red 11 food 12 | green 12 food 13 | blue 13 food 14 | green 14 food 15 | orange 15 food 16 | -------------------------------------------------------------------------------- /plugin/stack.vim: -------------------------------------------------------------------------------- 1 | " Utility Functions 2 | 3 | " Zip(list_a, list_b, method) 4 | " 5 | " join each element of list a with the corresponding element of list b 6 | " Use the third argument, method, to dictate how the elements should be 7 | " combined: 8 | " given: a = [a, b] 9 | " b = [1, 2] 10 | " 0 = flattened list : [a, 1, b, 2] 11 | " 1 = list groups : [[a, 1], [b, 2]] 12 | " x = join separator x : [ax1, bx2] 13 | " 14 | " NOTE: If one list is longer than the other, the tail of that list is added 15 | " to the result. 16 | function! Zip(a, b, ...) 17 | let method = 1 18 | if a:0 19 | let method = a:1 20 | endif 21 | let i = 0 22 | let r = [] 23 | let l_a = len(a:a) 24 | let l_b = len(a:b) 25 | let n = min([len(a:a), len(a:b)]) 26 | while i < n 27 | if method == "0" 28 | call add(r, a:a[i]) 29 | call add(r, a:b[i]) 30 | elseif method == "1" 31 | call add(r, [a:a[i], a:b[i]]) 32 | else 33 | call add(r, join([a:a[i], a:b[i]], method)) 34 | endif 35 | let i+= 1 36 | endwhile 37 | if l_a == l_b 38 | return r 39 | elseif l_a > l_b 40 | exe "return r + a:a[" . n . ":]" 41 | else 42 | exe "return r + a:b[" . n . ":]" 43 | endif 44 | endfunction 45 | 46 | function! ExtractPattern(str, pattern) 47 | let pattern = a:pattern 48 | let str = a:str 49 | let rlist = [] 50 | let ofs = 0 51 | let ms = match(str, pattern, ofs) 52 | while ms != -1 53 | let me = matchend(str, pattern, ofs) 54 | call add(rlist, strpart(str, ms, me-ms)) 55 | let ofs = ms+me 56 | let ms = match(str, pattern, ofs) 57 | endwhile 58 | return rlist 59 | endfunction 60 | 61 | function! Stack() 62 | let obj = {} 63 | let obj.stack = [] 64 | 65 | " 66 | 67 | func obj.empty() dict 68 | return empty(self.stack) 69 | endfunc 70 | 71 | func obj.size() dict 72 | return len(self.stack) 73 | endfunc 74 | 75 | func obj.pushsize() dict 76 | call self.push(len(self.stack)) 77 | return self 78 | endfunc 79 | 80 | 81 | func obj.map(str) dict 82 | call map(self.stack[-1], a:str) 83 | return self 84 | endfunc 85 | 86 | func obj.nmap(cnt, str) dict 87 | call map(self.stack[(self.size() - a:cnt):-1], a:str) 88 | return self 89 | endfunc 90 | 91 | func obj.mapall(str) dict 92 | call map(self.stack, a:str) 93 | return self 94 | endfunc 95 | 96 | func obj.filter(str) dict 97 | call filter(self.stack[-1], a:str) 98 | return self 99 | endfunc 100 | 101 | func obj.filter2(str) dict 102 | return self.filter('filter(v:val[1], ''' . a:str . ''') != []') 103 | endfunc 104 | 105 | func obj.nfilter(cnt, str) dict 106 | call filter(self.stack[(self.size() - a:cnt:-1], a:str) 107 | return self 108 | endfunc 109 | 110 | func obj.filterall(str) dict 111 | call filter(self.stack, a:str) 112 | return self 113 | endfunc 114 | 115 | func obj.sort(...) dict 116 | if a:0 117 | call sort(self.stack, a:1) 118 | else 119 | call sort(self.stack) 120 | endif 121 | return self 122 | endfunc 123 | 124 | func obj.reverse(...) dict 125 | let cnt = 1 126 | if a:0 127 | let cnt = a:1 128 | endif 129 | let x = self.pop(cnt) 130 | if cnt == 1 131 | call self.push(reverse(x)) 132 | else 133 | call self.pusheach(reverse(x)) 134 | endif 135 | return self 136 | endfunc 137 | 138 | func obj.range(n, m) dict 139 | return self.push(range(a:n, a:m)) 140 | endfunc 141 | 142 | func obj.rangeeach(n, m) dict 143 | return self.pusheach(range(a:n, a:m)) 144 | endfunc 145 | 146 | "--- 147 | 148 | func obj.flush() dict 149 | if self.size() > 0 150 | call remove(self.stack, 0, -1) 151 | endif 152 | return self 153 | endfunc 154 | 155 | func obj.push(o) dict 156 | call add(self.stack, a:o) 157 | return self 158 | endfunc 159 | 160 | " adds each element in list o separately to the top of the stack 161 | func obj.pusheach(o) dict 162 | call extend(self.stack, a:o) 163 | return self 164 | endfunc 165 | 166 | " TODO: these two functions share redundant code. DRY it 167 | func obj.pushline(start, ...) dict 168 | if a:0 169 | let lines = getline(a:start, a:1) 170 | else 171 | let lines = getline(a:start) 172 | endif 173 | return self.push(lines) 174 | endfunc 175 | 176 | " adds each line in range start,end separately to the top of the stack 177 | func obj.pusheachline(start, ...) dict 178 | if a:0 179 | let lines = getline(a:start, a:1) 180 | else 181 | let lines = getline(a:start) 182 | endif 183 | return self.pusheach(lines) 184 | endfunc 185 | 186 | func obj.pop(...) dict 187 | let cnt = 1 188 | if a:0 189 | let cnt = a:1 190 | endif 191 | if ! self.empty() 192 | if cnt <= 0 193 | return self.top() 194 | elseif cnt == 1 195 | return remove(self.stack, -1) 196 | else 197 | return remove(self.stack, self.size() - cnt, -1) 198 | endif 199 | else 200 | throw "Stack empty!" 201 | endif 202 | endfunc 203 | 204 | func obj.top() dict 205 | if ! self.empty() 206 | return self.stack[-1] 207 | else 208 | return "" 209 | endif 210 | endfunc 211 | 212 | " chainable printer 213 | func obj.p(...) dict 214 | let depth = self.size() 215 | if a:0 216 | let depth = a:1 217 | endif 218 | echo self.stack[(self.size()-depth):-1] 219 | return self 220 | endfunc 221 | 222 | " UNCHAINABLE: Returns a copy of the whole stack 223 | " could do with a better name? 224 | func obj.s() dict 225 | return deepcopy(self.stack) 226 | endfunc 227 | 228 | " UNCHAINABLE: Returns a copy of the whole stack 229 | func obj.string() dict 230 | return string(self.stack) 231 | endfunc 232 | 233 | " stack operations 234 | 235 | " NO EXPLICIT TESTS YET: 236 | func obj.swap() dict 237 | if self.size() >= 2 238 | let tmp = self.stack[-1] 239 | let self.stack[-1] = self.stack[-2] 240 | let self.stack[-2] = tmp 241 | endif 242 | return self 243 | endfunc 244 | 245 | " NO EXPLICIT TESTS YET: 246 | func obj.over() dict 247 | return self.push(self.stack[-2]) 248 | endfunc 249 | 250 | " NO EXPLICIT TESTS YET: 251 | " performs a left-rotation 252 | func obj.rot() dict 253 | if self.size() >= 3 254 | let x = self.stack[-3] 255 | let self.stack[-3] = self.stack[-2] 256 | let self.stack[-2] = self.stack[-1] 257 | let self.stack[-1] = x 258 | endif 259 | return self 260 | endfunc 261 | 262 | " performs a right-rotation 263 | func obj.rotate(...) dict 264 | let num_elems = 1 265 | let rot_span = 3 266 | if a:0 267 | let num_elems = a:1 268 | if a:0 == 2 269 | let rot_span = a:2 270 | endif 271 | endif 272 | if num_elems < rot_span 273 | let A = add([], self.pop(num_elems)) 274 | let B = self.pop(rot_span - len(num_elems)) 275 | call self.pusheach(A).pusheach(B) 276 | endif 277 | return self 278 | endfunc 279 | 280 | " NO EXPLICIT TESTS YET: 281 | func obj.drop() dict 282 | call self.pop() 283 | return self 284 | endfunc 285 | 286 | " NO EXPLICIT TESTS YET: 287 | " takes an optional count 288 | func obj.dup(...) dict 289 | let cnt = 1 290 | if a:0 291 | let cnt = a:1 292 | endif 293 | if cnt == 1 294 | return self.push(deepcopy(self.top())) 295 | else 296 | return self.pusheach(repeat([self.top()], a:cnt)) 297 | endif 298 | endfunc 299 | 300 | " NO EXPLICIT TESTS YET: 301 | " I need a better name for this - it keeps only the nominated elements on 302 | " the stack (counting from the top, upwards), dropping all below that 303 | " number. 304 | func obj.keep(...) dict 305 | let cnt = 1 306 | if a:0 307 | let cnt = a:1 308 | endif 309 | call remove(self.stack, 0, (self.size() - cnt)) 310 | return self 311 | endfunc 312 | 313 | " NO EXPLICIT TESTS YET: 314 | " by default, moves element at TOS to bottom of stack 315 | func obj.sink(...) dict 316 | let depth = 0 317 | if a:0 318 | let depth = a:1 319 | endif 320 | let x = self.pop() 321 | call insert(self.stack, x, depth) 322 | return self 323 | endfunc 324 | 325 | " numeric stack operations 326 | 327 | func obj.add() dict 328 | call self.push(self.pop() + self.pop()) 329 | return self 330 | endfunc 331 | 332 | func obj.sub() dict 333 | let x = self.pop() 334 | call self.push(self.pop() - x) 335 | return self 336 | endfunc 337 | 338 | func obj.mul() dict 339 | call self.push(self.pop() * self.pop()) 340 | return self 341 | endfunc 342 | 343 | func obj.div() dict 344 | let x = self.pop() 345 | call self.push(self.pop() / x) 346 | return self 347 | endfunc 348 | 349 | func obj.fdiv() dict 350 | let x = self.pop() 351 | call self.push((1.0 * self.pop()) / x) 352 | return self 353 | endfunc 354 | 355 | func obj.mod() dict 356 | let x = self.pop() 357 | call self.push(float2nr(self.pop()) % x) 358 | return self 359 | endfunc 360 | 361 | " sum the single list of numbers at TOS 362 | func obj.sum() dict 363 | return self.push(eval(join(self.pop(), '+'))) 364 | endfunc 365 | 366 | " sum the top numbers on the stack 367 | func obj.nsum(...) dict 368 | let cnt = 2 369 | if a:0 370 | let cnt = a:1 371 | endif 372 | let s = 0 373 | let c = 0 374 | while ! self.empty() && (c < cnt) 375 | let s += str2nr(self.pop()) 376 | let c += 1 377 | endwhile 378 | call self.push(s) 379 | return self 380 | endfunc 381 | 382 | " sum the whole stack 383 | func obj.sumall() dict 384 | return self.nsum(self.size()) 385 | endfunc 386 | 387 | 388 | " string stack operations 389 | 390 | func obj.join(...) dict 391 | let sep = '' 392 | if a:0 393 | let sep = a:1 394 | endif 395 | call self.push(join([self.pop(), self.pop()], sep)) 396 | return self 397 | endfunc 398 | 399 | func obj.njoin(cnt, ...) dict 400 | let sep = '' 401 | if a:0 402 | let sep = a:1 403 | endif 404 | let x = self.pop(a:cnt) 405 | call self.push(join(x, sep)) 406 | return self 407 | endfunc 408 | 409 | func obj.joinall(...) dict 410 | return call(self.njoin, [self.size()] + a:000, self) 411 | endfunc 412 | 413 | func obj.len() dict 414 | let o = self.pop() 415 | return self.push(len(o)) 416 | endfunc 417 | 418 | " UNTESTED: 419 | " works on single strings at TOS or a list of strings at TOS 420 | func obj.matchstr(pattern) dict 421 | let x = self.pop() 422 | if type(x) == type([]) 423 | return self.push(map(x, 'matchstr(v:val, a:pattern)')) 424 | else 425 | return self.push(matchstr(x, a:pattern)) 426 | endif 427 | endfunc 428 | 429 | " UNTESTED: 430 | " works on single strings at TOS or a list of strings at TOS 431 | func obj.matchlist(pattern) dict 432 | let x = self.pop() 433 | if type(x) == type([]) 434 | return self.push(map(x, 'matchlist(v:val, a:pattern)')) 435 | else 436 | return self.push(matchlist(x, a:pattern)) 437 | endif 438 | endfunc 439 | 440 | " UNTESTED: 441 | " works on single strings at TOS or a list of strings at TOS 442 | func obj.substitute(search, replace, flags) dict 443 | let x = self.pop() 444 | if type(x) == type([]) 445 | return self.push(map(x, 'substitute(v:val, a:search, a:replace, a:flags)')) 446 | else 447 | return self.push(substitute(x, a:search, a:replace, a:flags)) 448 | endif 449 | endfunc 450 | 451 | " list stack operations 452 | 453 | " turns cnt elements on top of the stack into a single list element at TOS 454 | func obj.list(...) dict 455 | let cnt = self.size() 456 | if a:0 457 | let cnt = a:1 458 | endif 459 | call self.push(self.pop(cnt)) 460 | return self 461 | endfunc 462 | 463 | " splits the string at TOS into a list 464 | func obj.split(...) dict 465 | let pat = '\s\+' 466 | if a:0 467 | let pat = a:1 468 | endif 469 | return self.push(split(self.pop(), pat)) 470 | endfunc 471 | 472 | func obj.explode() dict 473 | return self.pusheach(self.pop()) 474 | endfunc 475 | 476 | func obj.enumerate(...) dict 477 | let from = 1 478 | if a:0 479 | let from = a:1 480 | endif 481 | return self.map('[v:key+' . from . ', v:val]') 482 | endfunc 483 | 484 | func obj.extract(pattern) dict 485 | let x = self.pop() 486 | let rlist = [] 487 | if type(x) == type([]) 488 | for s in x 489 | call add(rlist, ExtractPattern(s, a:pattern)) 490 | endfor 491 | else 492 | let rlist = ExtractPattern(x, a:pattern) 493 | endif 494 | return self.push(rlist) 495 | endfunc 496 | 497 | " zips the top two elements (both expected to be equal-sized lists) together 498 | " takes an optional method - see Zip() at top of file for details 499 | func obj.zip(...) dict 500 | let method = 0 501 | if a:0 502 | let method = a:1 503 | endif 504 | let x = self.pop() 505 | return self.push(Zip(self.pop(), x, method)) 506 | endfunc 507 | 508 | func obj.unzip() dict 509 | return self.dup().map('v:val[0]').rotate().map('v:val[1]') 510 | endfunc 511 | 512 | " opt arg 1: method 513 | " opt arg 2: count of stack elements to bundle into each set 514 | func obj.zipset(...) dict 515 | let cnt = self.size() / 2 516 | let method = 0 517 | if a:0 518 | let method = a:1 519 | if a:0 == 2 520 | let cnt = a:2 521 | endif 522 | endif 523 | let x = self.pop(cnt) 524 | return self.push(Zip(self.pop(cnt), x, method)) 525 | endfunc 526 | 527 | " slice list at TOS into cnt slices each as separate lists on the stack 528 | func obj.slice(...) dict 529 | let x = deepcopy(self.pop()) 530 | let len = len(x) 531 | let cnt = len / 2 532 | if a:0 533 | let cnt = a:1 534 | endif 535 | let newlists = [] 536 | for idx in range(0, len, cnt) 537 | if idx < len 538 | if (cnt-1) > len(x) 539 | let cnt = len(x) 540 | endif 541 | call add(newlists, remove(x, 0, (cnt-1))) 542 | endif 543 | endfor 544 | call self.pusheach(newlists) 545 | return self 546 | endfunc 547 | 548 | " buffer interaction 549 | 550 | func obj.setline(...) dict 551 | let lnum = line('.') 552 | if a:0 553 | let lnum = a:1 554 | endif 555 | call setline(lnum, self.pop()) 556 | return self 557 | endfunc 558 | 559 | func obj.append(...) dict 560 | let lnum = line('.') 561 | if a:0 562 | let lnum = a:1 563 | endif 564 | call append(lnum, self.pop()) 565 | return self 566 | endfunc 567 | 568 | return obj 569 | endfunction 570 | 571 | if exists('_S') 572 | unlet _S 573 | endif 574 | let _S = Stack() 575 | 576 | " rotate the current visual block 577 | vnoremap R y:let @"=join(Stack().pusheach(split(@", "\n")).rotate(1,len(substitute(@", "[^\n]", '', 'g'))+1).s(), "\n")\|call setreg('"', '', 'ab')gvpgv 578 | 579 | -------------------------------------------------------------------------------- /test/001_maths.vim: -------------------------------------------------------------------------------- 1 | call vimtest#StartTap() 2 | call vimtap#Plan(8) " <== XXX Keep plan number updated. XXX 3 | 4 | let x = _S.push(10).push(10).add().top() 5 | call vimtap#Is(x, 20, 20, 'add') 6 | 7 | let x = _S.push(5).sub().top() 8 | call vimtap#Is(x, 15, 15, 'sub') 9 | 10 | let x = _S.push(5).div().top() 11 | call vimtap#Is(x, 3, 3, 'div') 12 | 13 | let x = _S.push(2).fdiv().top() 14 | call vimtap#Is(x, 1.5, 1.5, 'fdiv') 15 | 16 | let x = _S.push(4).mul().top() 17 | call vimtap#Is(x, 6, 6, 'mul') 18 | 19 | let x = _S.push(3).mod().top() 20 | call vimtap#Is(x, 0, 0, 'mod') 21 | 22 | let x = _S.flush().push(1).push(2).push(3).push(4).push(5).nsum(3).top() 23 | call vimtap#Is(x, 12, 12, 'nsum') 24 | 25 | let x = _S.flush().push(1).push(2).push(3).push(4).push(5).sumall().top() 26 | call vimtap#Is(x, 15, 15, 'sumall') 27 | 28 | call vimtest#Quit() 29 | -------------------------------------------------------------------------------- /test/002_strings.vim: -------------------------------------------------------------------------------- 1 | call vimtest#StartTap() 2 | call vimtap#Plan(5) " <== XXX Keep plan number updated. XXX 3 | 4 | let x = _S.push(10).push(10).join().top() 5 | call vimtap#Is(x, '1010', '1010', 'join') 6 | 7 | let x = _S.push(10).push(10).join(' ').top() 8 | call vimtap#Is(x, '10 10', '10 10', 'join with sep') 9 | 10 | call _S.flush() " start with a fresh stack 11 | let x = _S.pusheach(split("one two three four")).joinall(', ').top() 12 | call vimtap#Is(x, "one, two, three, four", "one, two, three, four", 'joinall') 13 | 14 | " an alternative is to use Stack's .split().explode() 15 | 16 | let x = _S.push("one two three four").split().explode().njoin(3, '-').top() 17 | call vimtap#Is(x, "two-three-four", "two-three-four", 'njoin') 18 | 19 | let x = _S.push("hello").len().top() 20 | call vimtap#Is(x, 5, 5, 'len') 21 | 22 | call vimtest#Quit() 23 | -------------------------------------------------------------------------------- /test/003_lists.vim: -------------------------------------------------------------------------------- 1 | call vimtest#StartTap() 2 | call vimtap#Plan(7) " <== XXX Keep plan number updated. XXX 3 | 4 | let x = _S.range(10, 20).top() 5 | call vimtap#Is(x, range(10,20), 'range(10,20)', 'range and list') 6 | 7 | unlet x 8 | let x = _S.rangeeach(10, 20).list(5).top() 9 | call vimtap#Is(x, range(16,20), 'range(15,20)', 'list(cnt)') 10 | 11 | unlet x 12 | call _S.flush() 13 | let x = _S.range(1,5).range(1,5).map('nr2char(64+v:val)').zip(1).map('v:val[0] . v:val[1]').top() 14 | call vimtap#Is(x, ['1A', '2B', '3C', '4D', '5E'], "['1A'...]", 'zip') 15 | 16 | unlet x 17 | call _S.flush() 18 | " We eventually want 10 digits on the stack, but .map() operates on the TOS 19 | " (expecting a list), so we just use .range() for the second set and then 20 | " .explode() it to get the individual digits. 21 | call _S.rangeeach(1,5).range(1,5).map('nr2char(64+v:val)').explode() 22 | let x = _S.zipset(1).map('v:val[0] . v:val[1]').top() 23 | call vimtap#Is(x, ['1A', '2B', '3C', '4D', '5E'], "['1A'...]", 'zip') 24 | 25 | " again now without flushing the stack, so ['1A', '2B', ...] is on top 26 | " and we want to avoid including that in the zipset 27 | unlet x 28 | call _S.rangeeach(1,5).range(1,5).map('nr2char(64+v:val)').explode() 29 | let x = _S.zipset(1,5).map('v:val[0] . v:val[1]').top() 30 | call vimtap#Is(x, ['1A', '2B', '3C', '4D', '5E'], "['1A'...]", 'zip') 31 | 32 | " reverse the top element (a list) on the stack 33 | unlet x 34 | let x = _S.reverse().zip(':').top() 35 | call vimtap#Is(x, ['1A:5E', '2B:4D', '3C:3C', '4D:2B', '5E:1A'], "['1A:5E'...]", 'zip') 36 | call _S.flush() 37 | unlet x 38 | let x = _S.rangeeach(1,10).reverse(5).s() 39 | call vimtap#Is(x, [1,2,3,4,5,10,9,8,7,6], "[1,2,3,4,5,10,9,8,7,6]", 'reverse(cnt)') 40 | 41 | call vimtest#Quit() 42 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | The plugins runVimTests (http://www.vim.org/scripts/script.php?script_id=2565) 2 | and VimTAP (http://www.vim.org/scripts/script.php?script_id=2213) are needed to 3 | run these tests. 4 | 5 | Besides the _setup.vim configuration file present in this repo you need to 6 | create a global one and place it in the same dir where the runVimTests 7 | executable is located. Assuming the executable is at '~/bin/runVimTests' this 8 | global configuration file should be '~/bin/runVimTestsSetup.vim' and should 9 | have something like the following lines inside of it: 10 | 11 | " Prepend tests repos to &rtp 12 | let &runtimepath = '/path/to/runVimTests_dir,' . &rtp 13 | let &runtimepath = '/path/to/vimTAP_dir,' . &rtp 14 | -------------------------------------------------------------------------------- /test/_setup.vim: -------------------------------------------------------------------------------- 1 | let &rtp = expand(':p:h:h') . ',' . &rtp . ',' . expand(':p:h:h') . '/after' 2 | 3 | runtime plugin/stack.vim 4 | --------------------------------------------------------------------------------