| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  | " LogiPat: Boolean logical pattern matcher | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  | "   Author:  Charles E. Campbell | 
					
						
							| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  | "   Date:    Apr 04, 2016 | 
					
						
							|  |  |  | "   Version: 4 | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  | "   Purpose: to do Boolean-logic based regular expression pattern matching | 
					
						
							|  |  |  | " Copyright:    Copyright (C) 1999-2011 Charles E. Campbell {{{1 | 
					
						
							|  |  |  | "               Permission is hereby granted to use and distribute this code, | 
					
						
							|  |  |  | "               with or without modifications, provided that this copyright | 
					
						
							|  |  |  | "               notice is copied with it. Like most anything else that's free, | 
					
						
							|  |  |  | "               LogiPat.vim is provided *as is* and comes with no warranty | 
					
						
							|  |  |  | "               of any kind, either expressed or implied. By using this | 
					
						
							|  |  |  | "               plugin, you agree that in no event will the copyright | 
					
						
							|  |  |  | "               holder be liable for any damages resulting from the use | 
					
						
							|  |  |  | "               of this software. | 
					
						
							|  |  |  | " | 
					
						
							|  |  |  | "   Usage: {{{1 | 
					
						
							|  |  |  | "       :LogiPat ... | 
					
						
							|  |  |  | " | 
					
						
							|  |  |  | "         Boolean logic supported: | 
					
						
							|  |  |  | "            () grouping operators | 
					
						
							|  |  |  | "            !  not the following pattern | 
					
						
							|  |  |  | "            |  logical or | 
					
						
							|  |  |  | "            &  logical and | 
					
						
							|  |  |  | "            "..pattern.." | 
					
						
							|  |  |  | "	Example: {{{1 | 
					
						
							|  |  |  | "		:LogiPat !("january"|"february") | 
					
						
							|  |  |  | "		  would match all strings not containing the strings january | 
					
						
							|  |  |  | "		  or february | 
					
						
							|  |  |  | "	GetLatestVimScripts: 1290 1 :AutoInstall: LogiPat.vim | 
					
						
							|  |  |  | " | 
					
						
							|  |  |  | "  Behold, you will conceive in your womb, and bring forth a son, {{{1 | 
					
						
							|  |  |  | "  and will call his name Jesus. He will be great, and will be | 
					
						
							|  |  |  | "  called the Son of the Most High. The Lord God will give him the | 
					
						
							|  |  |  | "  throne of his father, David, and he will reign over the house of | 
					
						
							|  |  |  | "  Jacob forever. There will be no end to his kingdom. (Luke 1:31-33 WEB) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " Load Once: {{{1 | 
					
						
							| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  | if &cp || exists("loaded_logiPat") | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  |  finish | 
					
						
							|  |  |  | endif | 
					
						
							| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  | let g:loaded_logiPat = "v4" | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  | let s:keepcpo        = &cpo | 
					
						
							|  |  |  | set cpo&vim | 
					
						
							|  |  |  | "DechoRemOn | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " Public Interface: {{{1 | 
					
						
							| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  | com!     -nargs=* LogiPat		call   LogiPat(<q-args>,1) | 
					
						
							|  |  |  | sil! com -nargs=* LP			call   LogiPat(<q-args>,1) | 
					
						
							|  |  |  | sil! com -nargs=* LPR			call   LogiPat(<q-args>,1,"r") | 
					
						
							|  |  |  | com!     -nargs=+ LPE			echomsg LogiPat(<q-args>) | 
					
						
							|  |  |  | com!     -nargs=+ LogiPatFlags	let  s:LogiPatFlags="<args>" | 
					
						
							|  |  |  | sil! com -nargs=+ LPF			let  s:LogiPatFlags="<args>" | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | " ===================================================================== | 
					
						
							|  |  |  | " Functions: {{{1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LogiPat: this function interprets the boolean-logic pattern {{{2 | 
					
						
							|  |  |  | fun! LogiPat(pat,...) | 
					
						
							|  |  |  | "  call Dfunc("LogiPat(pat<".a:pat.">)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " LogiPat(pat,dosearch) | 
					
						
							|  |  |  |   if a:0 > 0 | 
					
						
							|  |  |  |    let dosearch= a:1 | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |    let dosearch= 0 | 
					
						
							|  |  |  |   endif | 
					
						
							| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  |   if a:0 >= 3 | 
					
						
							|  |  |  |    let s:LogiPatFlags= a:3 | 
					
						
							|  |  |  |   endif | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   let s:npatstack = 0 | 
					
						
							|  |  |  |   let s:nopstack  = 0 | 
					
						
							|  |  |  |   let s:preclvl   = 0 | 
					
						
							|  |  |  |   let expr        = a:pat | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " Lexer/Parser | 
					
						
							|  |  |  |   while expr != "" | 
					
						
							|  |  |  | "   call Decho("expr<".expr.">") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    if expr =~ '^"' | 
					
						
							|  |  |  | 	" push a Pattern; accept "" as a single " in the pattern | 
					
						
							|  |  |  |     let expr = substitute(expr,'^\s*"','','') | 
					
						
							|  |  |  |     let pat  = substitute(expr,'^\(\%([^"]\|\"\"\)\{-}\)"\([^"].*$\|$\)','\1','') | 
					
						
							|  |  |  | 	let pat  = substitute(pat,'""','"','g') | 
					
						
							|  |  |  |     let expr = substitute(expr,'^\(\%([^"]\|\"\"\)\{-}\)"\([^"].*$\|$\)','\2','') | 
					
						
							|  |  |  |     let expr = substitute(expr,'^\s*','','') | 
					
						
							|  |  |  | "    call Decho("pat<".pat."> expr<".expr.">") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     call s:LP_PatPush('.*'.pat.'.*') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    elseif expr =~ '^[!()|&]' | 
					
						
							|  |  |  |     " push an operator | 
					
						
							|  |  |  |     let op   = strpart(expr,0,1) | 
					
						
							|  |  |  |     let expr = strpart(expr,strlen(op)) | 
					
						
							|  |  |  | 	" allow for those who can't resist doubling their and/or operators | 
					
						
							|  |  |  | 	if op =~ '[|&]' && expr[0] == op | 
					
						
							|  |  |  |      let expr = strpart(expr,strlen(op)) | 
					
						
							|  |  |  | 	endif | 
					
						
							|  |  |  |     call s:LP_OpPush(op) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    elseif expr =~ '^\s' | 
					
						
							|  |  |  |     " skip whitespace | 
					
						
							|  |  |  |     let expr= strpart(expr,1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    else | 
					
						
							|  |  |  |     echoerr "operator<".strpart(expr,0,1)."> not supported (yet)" | 
					
						
							|  |  |  |     let expr= strpart(expr,1) | 
					
						
							|  |  |  |    endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   endwhile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " Final Execution | 
					
						
							|  |  |  |   call s:LP_OpPush('Z') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let result= s:LP_PatPop(1) | 
					
						
							|  |  |  | "  call Decho("result=".result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " sanity checks and cleanup | 
					
						
							|  |  |  |   if s:npatstack > 0 | 
					
						
							|  |  |  |    echoerr s:npatstack." patterns left on stack!" | 
					
						
							|  |  |  |    let s:npatstack= 0 | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  |   if s:nopstack > 0 | 
					
						
							|  |  |  |    echoerr s:nopstack." operators left on stack!" | 
					
						
							|  |  |  |    let s:nopstack= 0 | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " perform the indicated search | 
					
						
							|  |  |  |   if dosearch | 
					
						
							| 
									
										
										
										
											2016-04-10 01:31:25 +02:00
										 |  |  |    if exists("s:LogiPatFlags") && s:LogiPatFlags != "" | 
					
						
							| 
									
										
										
										
											2015-06-19 18:48:41 +02:00
										 |  |  | "  call Decho("search(result<".result."> LogiPatFlags<".s:LogiPatFlags.">)") | 
					
						
							|  |  |  |     call search(result,s:LogiPatFlags) | 
					
						
							|  |  |  |    else | 
					
						
							|  |  |  | "  call Decho("search(result<".result.">)") | 
					
						
							|  |  |  |     call search(result) | 
					
						
							|  |  |  |    endif | 
					
						
							|  |  |  |    let @/= result | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "  call Dret("LogiPat ".result) | 
					
						
							|  |  |  |   return result | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " s:String: Vim6.4 doesn't have string() {{{2 | 
					
						
							|  |  |  | func! s:String(str) | 
					
						
							|  |  |  |   return "'".escape(a:str, '"')."'" | 
					
						
							|  |  |  | endfunc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_PatPush: {{{2 | 
					
						
							|  |  |  | fun! s:LP_PatPush(pat) | 
					
						
							|  |  |  | "  call Dfunc("LP_PatPush(pat<".a:pat.">)") | 
					
						
							|  |  |  |   let s:npatstack              = s:npatstack + 1 | 
					
						
							|  |  |  |   let s:patstack_{s:npatstack} = a:pat | 
					
						
							|  |  |  | "  call s:StackLook("patpush") "Decho | 
					
						
							|  |  |  | "  call Dret("LP_PatPush : npatstack=".s:npatstack) | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_PatPop: pop a number/variable from LogiPat's pattern stack {{{2 | 
					
						
							|  |  |  | fun! s:LP_PatPop(lookup) | 
					
						
							|  |  |  | "  call Dfunc("LP_PatPop(lookup=".a:lookup.")") | 
					
						
							|  |  |  |   if s:npatstack > 0 | 
					
						
							|  |  |  |    let ret         = s:patstack_{s:npatstack} | 
					
						
							|  |  |  |    let s:npatstack = s:npatstack - 1 | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |    let ret= "---error---" | 
					
						
							|  |  |  |    echoerr "(LogiPat) invalid expression" | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | "  call s:StackLook("patpop") "Decho | 
					
						
							|  |  |  | "  call Dret("LP_PatPop ".ret) | 
					
						
							|  |  |  |   return ret | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_OpPush: {{{2 | 
					
						
							|  |  |  | fun! s:LP_OpPush(op) | 
					
						
							|  |  |  | "  call Dfunc("LP_OpPush(op<".a:op.">)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " determine new operator's precedence level | 
					
						
							|  |  |  |   if a:op == '(' | 
					
						
							|  |  |  |   	let s:preclvl= s:preclvl + 10 | 
					
						
							|  |  |  | 	let preclvl  = s:preclvl | 
					
						
							|  |  |  |   elseif a:op == ')' | 
					
						
							|  |  |  |   	let s:preclvl= s:preclvl - 10 | 
					
						
							|  |  |  |    if s:preclvl < 0 | 
					
						
							|  |  |  |     let s:preclvl= 0 | 
					
						
							|  |  |  |     echoerr "too many )s" | 
					
						
							|  |  |  |    endif | 
					
						
							|  |  |  |    let preclvl= s:preclvl | 
					
						
							|  |  |  |   elseif a:op =~ '|' | 
					
						
							|  |  |  |    let preclvl= s:preclvl + 2 | 
					
						
							|  |  |  |   elseif a:op =~ '&' | 
					
						
							|  |  |  |    let preclvl= s:preclvl + 4 | 
					
						
							|  |  |  |   elseif a:op == '!' | 
					
						
							|  |  |  |    let preclvl= s:preclvl + 6 | 
					
						
							|  |  |  |   elseif a:op == 'Z' | 
					
						
							|  |  |  |    let preclvl= -1 | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |    echoerr "expr<".expr."> not supported (yet)" | 
					
						
							|  |  |  |    let preclvl= s:preclvl | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | "  call Decho("new operator<".a:op."> preclvl=".preclvl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " execute higher-precdence operators | 
					
						
							|  |  |  | "  call Decho("execute higher-precedence operators") | 
					
						
							|  |  |  |   call s:LP_Execute(preclvl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " push new operator onto operator-stack | 
					
						
							|  |  |  | "  call Decho("push new operator<".a:op."> onto stack with preclvl=".preclvl." at nopstack=".(s:nopstack+1)) | 
					
						
							|  |  |  |   if a:op =~ '!' | 
					
						
							|  |  |  |    let s:nopstack             = s:nopstack + 1 | 
					
						
							|  |  |  |    let s:opprec_{s:nopstack}  = preclvl | 
					
						
							|  |  |  |    let s:opstack_{s:nopstack} = a:op | 
					
						
							|  |  |  |   elseif a:op =~ '|' | 
					
						
							|  |  |  |    let s:nopstack             = s:nopstack + 1 | 
					
						
							|  |  |  |    let s:opprec_{s:nopstack}  = preclvl | 
					
						
							|  |  |  |    let s:opstack_{s:nopstack} = a:op | 
					
						
							|  |  |  |   elseif a:op == '&' | 
					
						
							|  |  |  |    let s:nopstack             = s:nopstack + 1 | 
					
						
							|  |  |  |    let s:opprec_{s:nopstack}  = preclvl | 
					
						
							|  |  |  |    let s:opstack_{s:nopstack} = a:op | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "  call s:StackLook("oppush") "Decho | 
					
						
							|  |  |  | "  call Dret("LP_OpPush : s:preclvl=".s:preclvl) | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_Execute: execute operators from opstack using pattern stack {{{2 | 
					
						
							|  |  |  | fun! s:LP_Execute(preclvl) | 
					
						
							|  |  |  | "  call Dfunc("LP_Execute(preclvl=".a:preclvl.") npatstack=".s:npatstack." nopstack=".s:nopstack) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " execute all higher precedence operators | 
					
						
							|  |  |  |   while s:nopstack > 0 && a:preclvl < s:opprec_{s:nopstack} | 
					
						
							|  |  |  |    let op= s:opstack_{s:nopstack} | 
					
						
							|  |  |  | "   call Decho("op<".op."> nop=".s:nopstack." [preclvl=".a:preclvl."] < [opprec_".s:nopstack."=".s:opprec_{s:nopstack}."]") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    let s:nopstack = s:nopstack - 1 | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |    if     op == '!' | 
					
						
							|  |  |  |     let n1= s:LP_PatPop(1) | 
					
						
							|  |  |  | 	call s:LP_PatPush(s:LP_Not(n1)) | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |    elseif op == '|' | 
					
						
							|  |  |  |     let n1= s:LP_PatPop(1) | 
					
						
							|  |  |  |     let n2= s:LP_PatPop(1) | 
					
						
							|  |  |  |     call s:LP_PatPush(s:LP_Or(n2,n1)) | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |    elseif op =~ '&' | 
					
						
							|  |  |  |     let n1= s:LP_PatPop(1) | 
					
						
							|  |  |  |     let n2= s:LP_PatPop(1) | 
					
						
							|  |  |  |     call s:LP_PatPush(s:LP_And(n2,n1)) | 
					
						
							|  |  |  |    endif | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  | "   call s:StackLook("execute") "Decho | 
					
						
							|  |  |  |   endwhile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "  call Dret("LP_Execute") | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_Not: writes a logical-not for a pattern {{{2 | 
					
						
							|  |  |  | fun! s:LP_Not(pat) | 
					
						
							|  |  |  | "  call Dfunc("LP_Not(pat<".a:pat.">)") | 
					
						
							|  |  |  |   if a:pat =~ '^\.\*' && a:pat =~ '\.\*$' | 
					
						
							|  |  |  |    let pat= substitute(a:pat,'^\.\*\(.*\)\.\*$','\1','') | 
					
						
							|  |  |  |    let ret= '^\%(\%('.pat.'\)\@!.\)*$' | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |    let ret= '^\%(\%('.a:pat.'\)\@!.\)*$' | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | "  call Dret("LP_Not ".ret) | 
					
						
							|  |  |  |   return ret | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_Or: writes a logical-or branch using two patterns {{{2 | 
					
						
							|  |  |  | fun! s:LP_Or(pat1,pat2) | 
					
						
							|  |  |  | "  call Dfunc("LP_Or(pat1<".a:pat1."> pat2<".a:pat2.">)") | 
					
						
							|  |  |  |   let ret= '\%('.a:pat1.'\|'.a:pat2.'\)' | 
					
						
							|  |  |  | "  call Dret("LP_Or ".ret) | 
					
						
							|  |  |  |   return ret | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " LP_And: writes a logical-and concat using two patterns {{{2 | 
					
						
							|  |  |  | fun! s:LP_And(pat1,pat2) | 
					
						
							|  |  |  | "  call Dfunc("LP_And(pat1<".a:pat1."> pat2<".a:pat2.">)") | 
					
						
							|  |  |  |   let ret= '\%('.a:pat1.'\&'.a:pat2.'\)' | 
					
						
							|  |  |  | "  call Dret("LP_And ".ret) | 
					
						
							|  |  |  |   return ret | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | " StackLook: {{{2 | 
					
						
							|  |  |  | fun! s:StackLook(description) | 
					
						
							|  |  |  | "  call Dfunc("StackLook(description<".a:description.">)") | 
					
						
							|  |  |  |   let iop = 1 | 
					
						
							|  |  |  |   let ifp = 1 | 
					
						
							|  |  |  | "  call Decho("Pattern                       Operator") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " print both pattern and operator | 
					
						
							|  |  |  |   while ifp <= s:npatstack && iop <= s:nopstack | 
					
						
							|  |  |  |    let fp = s:patstack_{ifp} | 
					
						
							|  |  |  |    let op = s:opstack_{iop}." (P".s:opprec_{s:nopstack}.')' | 
					
						
							|  |  |  |    let fplen= strlen(fp) | 
					
						
							|  |  |  |    if fplen < 30 | 
					
						
							|  |  |  |    	let fp= fp.strpart("                              ",1,30-fplen) | 
					
						
							|  |  |  |    endif | 
					
						
							|  |  |  | "   call Decho(fp.op) | 
					
						
							|  |  |  |    let ifp = ifp + 1 | 
					
						
							|  |  |  |    let iop = iop + 1 | 
					
						
							|  |  |  |   endwhile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " print just pattern | 
					
						
							|  |  |  |   while ifp <= s:npatstack | 
					
						
							|  |  |  |    let fp  = s:patstack_{ifp} | 
					
						
							|  |  |  | "   call Decho(fp) | 
					
						
							|  |  |  |    let ifp = ifp + 1 | 
					
						
							|  |  |  |   endwhile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " print just operator | 
					
						
							|  |  |  |   while iop <= s:nopstack | 
					
						
							|  |  |  |    let op  = s:opstack_{iop}." (P".s:opprec_{s:nopstack}.')' | 
					
						
							|  |  |  | "   call Decho("                              ".op) | 
					
						
							|  |  |  |    let iop = iop + 1 | 
					
						
							|  |  |  |   endwhile | 
					
						
							|  |  |  | "  call Dret("StackLook") | 
					
						
							|  |  |  | endfun | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " --------------------------------------------------------------------- | 
					
						
							|  |  |  | "  Cleanup And Modeline: {{{1 | 
					
						
							|  |  |  | let &cpo= s:keepcpo | 
					
						
							|  |  |  | unlet s:keepcpo | 
					
						
							|  |  |  | " vim: ts=4 fdm=marker |