home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / perl / search.pl < prev    next >
Perl Script  |  1998-05-27  |  6KB  |  245 lines

  1. package Vile::Search;
  2.  
  3. #
  4. # This package contains perl subroutines which are intended as a
  5. # drop in replacement for vile's search facilities.  Not all features
  6. # (such as visual matches) are implemented yet.
  7. #
  8. # These bindings are proper motions, however, so things like 'dn'
  9. # or 'd/foo' will work as expected.
  10. #
  11. # Usage
  12. # -----
  13. # :perl require "search.pl";
  14.  
  15. *CHUNKSIZE = \100;
  16. my $direction = '';
  17.  
  18. #open TTY, ">/dev/tty";            # for debugging
  19.  
  20. #
  21. # Get the pattern to search for, either from the user or from the
  22. # previously stashed value in vile's search variable.
  23. #
  24.  
  25. sub getpat {
  26.     my ($how, $ldirection) = @_;
  27.  
  28.     if (defined($how) and $how eq 'noprompt') {
  29.     $pat = Vile::get('search')
  30.     }
  31.     else {
  32.     $direction = $ldirection;
  33.     $pat = Vile::mlreply_no_opts($direction eq 'forward' 
  34.                                    ? 'Perl search: ' 
  35.                        : 'Reverse perl search: ',
  36.                                      scalar Vile::get('search'));
  37.     }
  38.     Vile::set(search => $pat) if defined($pat);
  39.     return $pat;
  40. }
  41.  
  42. #
  43. # Back references (e.g, \1, \2, etc.) need to be adjusted to work
  44. # properly since our search expression contains parenthesized
  45. # expressions too.
  46. #
  47.  
  48. sub fixbackreferences {
  49.     my $pat = shift;
  50.     my $adj = shift;
  51.     my $lpcount = 0;        # number of unescaped left parens
  52.  
  53.     $lpcount++ while $pat =~ /(^|[^\\])\(/g;
  54.  
  55.     if ($lpcount > 0) {
  56.     $pat =~ s/\\(\[1-9][0-9]*|.)/
  57.              "\\" . (($1 + 0 && $1 <= $lpcount) ? $1+$adj : $1)/gex;
  58.     }
  59.  
  60.     return $pat;
  61. }
  62.  
  63. #
  64. # Search forward.  This is not as straightforward as it could be since
  65. # we attempt to fetch the lines in chunks for efficient searching.
  66. #
  67.  
  68. sub fsearch {
  69.     my $pat = getpat(shift, 'forward');
  70.     
  71.     return 0 unless defined($pat);
  72.  
  73.     my $wrap       = 0;
  74.     my $cb         = $Vile::current_buffer;
  75.     my @start_dot  = $cb->current_position;
  76.     my $lastline   = ($cb->setregion(1,'$'))[2];
  77.     my $chunkstart = $start_dot[0];
  78.     my $pos        = $start_dot[1]+1;
  79.  
  80.     if ($pos >= ($cb->setregion($chunkstart,0,$chunkstart,'$$'))[3]) {
  81.     $pos = 0;
  82.     $chunkstart++;
  83.     $chunkstart = 1 if ($chunkstart > $lastline);
  84.     }
  85.  
  86.     my $chunkend   = $chunkstart + $CHUNKSIZE;
  87.  
  88.     $pat = fixbackreferences($pat, 1);
  89.  
  90.     while (1) {
  91.     $cb->set_region($chunkstart, $chunkend-1);
  92.     $chunk = $cb->fetch;
  93.     pos($chunk) = $pos;
  94.     $pos = 0;
  95.     if ($chunk =~ /($pat)/mg) {
  96.         my $lc = 0;
  97.         my $matchlen = length($1);
  98.         $chunk = substr($chunk, 0, pos($chunk));
  99.         $lc++ while $chunk =~ /\n/g;
  100.         $chunk =~ s/.*\n//g;
  101.         $cb->current_position($chunkstart + $lc, length($chunk) - $matchlen);
  102.         if ($wrap) {
  103.         @dot = $cb->current_position;
  104.         if ($start_dot[0] == $dot[0] and $start_dot[1] == $dot[1]) {
  105.             print "Only one occurence of pattern";
  106.         }
  107.         else {
  108.             print "[Search wrapped past end of buffer]";
  109.         }
  110.         }
  111.         return 1;
  112.     }
  113.     }
  114.     continue {
  115.     $chunkstart = $chunkend;
  116.     if ($wrap) {
  117.         last if $chunkstart > $start_dot[0];
  118.     }
  119.     elsif ($chunkstart > $lastline) {
  120.         $wrap = 1;
  121.         $chunkstart = 1;
  122.     }
  123.     $chunkend = $chunkstart + $CHUNKSIZE;
  124.     }
  125.  
  126.     print "Not found";
  127.     return 0;
  128. }
  129.  
  130. #
  131. # Search backward
  132. #
  133.  
  134. sub rsearch {
  135.     my $pat = getpat(shift, 'backward');
  136.     return 0 unless defined($pat);
  137.  
  138.     my $wrap       = 0;
  139.     my $cb         = $Vile::current_buffer;
  140.     my @start_dot  = $cb->current_position;
  141.     my $lastline   = ($cb->setregion(1,'$'))[2];
  142.     my $chunkend   = $start_dot[0]+1;
  143.     my $pmpat;
  144.  
  145.  
  146.     if ($start_dot[1] == 0) {
  147.     if ($chunkend <= 2) {
  148.         $chunkend = $lastline+1;
  149.     }
  150.     else {
  151.         $chunkend--;
  152.     }
  153.     $pmpat = '.*';
  154.     }
  155.     else {
  156.     $pmpat = ".{0,@{[$start_dot[1]-1]}}";
  157.     }
  158.  
  159.     my $chunkstart = $chunkend - 1;
  160.  
  161.     $chunkstart = 1 unless $chunkstart > 0;
  162.     $cb->set_region($chunkstart, 0, $chunkend-1, '$');
  163.  
  164.     $pat = fixbackreferences($pat, 2);
  165.  
  166.     # $ matches at both the newline and the position after the newline.
  167.     # Eliminate one of these cases.
  168.     $pat =~ s/(^|[^\\])\$$/$1(?=\n\$)/;
  169.  
  170.     while (1) {
  171.     $chunk = $cb->fetch;
  172.     pos($chunk) = $pos;
  173.     $pos = 0;
  174.     if (my ($prematch, $match) = $chunk =~ /\A($pmpat)($pat)/mg) {
  175.         my $lc = 0;
  176.         $lc++ while ($prematch =~ /\n/g);
  177.         $prematch =~ s/.*\n//g;
  178.         $cb->current_position($chunkstart + $lc, length($prematch) );
  179.         if ($wrap) {
  180.         @dot = $cb->current_position;
  181.         if ($start_dot[0] == $dot[0] and $start_dot[1] == $dot[1]) {
  182.             print "Only one occurence of pattern";
  183.         }
  184.         else {
  185.             print "[Search wrapped past end of buffer]";
  186.         }
  187.         }
  188.         return 1;
  189.     }
  190.     }
  191.     continue {
  192.     $chunkend = $chunkstart;
  193.     if ($wrap) {
  194.         last if $chunkend <= $start_dot[0];
  195.     }
  196.     elsif ($chunkend <= 1) {
  197.         $wrap = 1;
  198.         $chunkend = $lastline + 1;
  199.     }
  200.     $chunkstart = $chunkend - $CHUNKSIZE;
  201.     $chunkstart = 1 unless $chunkstart > 0;
  202.     $cb->set_region($chunkstart, $chunkend-1);
  203.     $pmpat = "[\000-\377]*";
  204.     }
  205.  
  206.     print "Not found";
  207.     return 0;
  208. }
  209.  
  210. #
  211. # Find next occurrence of pattern in the current direction
  212. #
  213.  
  214. sub searchnext {
  215.     $direction eq 'forward' ? fsearch('noprompt') : rsearch('noprompt');
  216. }
  217.  
  218. #
  219. # Find previous occurrence of pattern in current direction
  220. #
  221.  
  222. sub searchprev {
  223.     $direction eq 'forward' ? rsearch('noprompt') : fsearch('noprompt');
  224. }
  225.  
  226. #
  227. # Register the above as Vile procedures
  228. #
  229.  
  230. Vile::register_motion 'perl-fsearch' => \&fsearch, "Forward search with perl";
  231. Vile::register_motion 'perl-rsearch' => \&rsearch, "Reverse search with perl";
  232. Vile::register_motion 'perl-search-next' => \&searchnext, "Search next";
  233. Vile::register_motion 'perl-search-prev' => \&searchprev, "Search prev";
  234.  
  235. #
  236. # Set up the standard keybindings
  237. #
  238.  
  239. Vile::command("bind-key perl-fsearch /");
  240. Vile::command("bind-key perl-rsearch ?");
  241. Vile::command("bind-key perl-search-next n");
  242. Vile::command("bind-key perl-search-prev N");
  243.  
  244. 1;
  245.