home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / fences.c < prev    next >
C/C++ Source or Header  |  1998-07-16  |  9KB  |  448 lines

  1. /*
  2.  *
  3.  *    fences.c
  4.  *
  5.  * Match up various fenceposts, like (), [], {}, */ /*, #if, #el, #en
  6.  *
  7.  * Most code probably by Dan Lawrence or Dave Conroy for MicroEMACS
  8.  * Extensions for vile by Paul Fox
  9.  * Revised to use regular expressions - T.Dickey
  10.  *
  11.  * $Header: /usr/build/vile/vile/RCS/fences.c,v 1.50 1998/07/17 01:28:23 tom Exp $
  12.  *
  13.  */
  14.  
  15. #include    "estruct.h"
  16. #include    "edef.h"
  17.  
  18. #if OPT_CFENCE
  19.  
  20. #define    CPP_UNKNOWN -1
  21. #define    CPP_IF       0
  22. #define    CPP_ELIF     1
  23. #define    CPP_ELSE     2
  24. #define    CPP_ENDIF    3
  25.  
  26. #define ok_CPP(n) ((n) >= CPP_IF && (n) <= CPP_ENDIF)
  27.  
  28. #define BLK_UNKNOWN -1
  29. #define BLK_BEGIN    4
  30. #define BLK_END      5
  31.  
  32. #define ok_BLK(n) ((n) >= BLK_BEGIN && (n) <= BLK_END)
  33.  
  34. #define COMPLEX_FENCE_CH  -4
  35. #define COMMENT_FENCE_CH  -3
  36. #define PAIRED_FENCE_CH   -2
  37. #define UNKNOWN_FENCE_CH  -1
  38.  
  39. #define S_COL(exp) (C_NUM)(exp->startp[0] - DOT.l->l_text)
  40. #define E_COL(exp) (C_NUM)(exp->endp[0]   - DOT.l->l_text)
  41.  
  42. #define BlkBegin b_val_rexp(curbp, VAL_FENCE_BEGIN)->reg
  43. #define BlkEnd   b_val_rexp(curbp, VAL_FENCE_END)->reg
  44.  
  45. #define CurrentChar() \
  46.      (is_at_end_of_line(DOT) ? '\n' : char_at(DOT))
  47. #define InDirection(sdir) \
  48.      ((sdir == REVERSE) ? backchar(FALSE, 1) : forwchar(FALSE, 1))
  49.  
  50. static int
  51. match_complex(LINE *lp)
  52. {
  53.     static int modes[] = { CPP_IF, CPP_ELIF, CPP_ELSE, CPP_ENDIF };
  54.     size_t j, k;
  55.  
  56.     for (j = 0; j < TABLESIZE(modes); j++) {
  57.         /* fix for CC_CANNOT_OFFSET_CASES */
  58.         switch (modes[j]) {
  59.         case CPP_IF:        k = VAL_FENCE_IF;    break;
  60.         case CPP_ELIF:        k = VAL_FENCE_ELIF;    break;
  61.         case CPP_ELSE:        k = VAL_FENCE_ELSE;    break;
  62.         case CPP_ENDIF:        k = VAL_FENCE_FI;    break;
  63.         default:                    continue;
  64.         }
  65.         if (lregexec(b_val_rexp(curbp, k)->reg, lp, 0, llength(lp)))
  66.             return modes[j];
  67.     }
  68.  
  69.     return CPP_UNKNOWN;
  70. }
  71.  
  72. /*
  73.  * Find the match, if any, for a begin/end comment marker.  If we find a
  74.  * match, the regular expression will overlap the given LINE/offset.
  75.  */
  76. static int
  77. match_simple(void)
  78. {
  79.     C_NUM first = 0;
  80.     C_NUM last = llength(DOT.l);
  81.  
  82.     for (first = 0; first < last; first = S_COL(BlkBegin) + 1) {
  83.         if (!lregexec(BlkBegin, DOT.l, first, last))
  84.             break;
  85.         if ((S_COL(BlkBegin) <= DOT.o)
  86.          && (E_COL(BlkBegin) >  DOT.o))
  87.             return BLK_BEGIN;
  88.     }
  89.  
  90.     for (first = 0; first < last && DOT.o <= last; last = E_COL(BlkEnd) - 1) {
  91.         if (!lregexec(BlkEnd, DOT.l, first, last))
  92.             break;
  93.         if ((S_COL(BlkEnd) <= DOT.o)
  94.          && (E_COL(BlkEnd) >  DOT.o))
  95.             return BLK_END;
  96.         if (last >= E_COL(BlkEnd) - 1)
  97.             break;
  98.     }
  99.  
  100.     return BLK_UNKNOWN;
  101. }
  102.  
  103. static int
  104. complex_fence(int sdir, int key)
  105. {
  106.     int count = 1;
  107.     int that = CPP_UNKNOWN;
  108.  
  109.     /* patch: this should come from arguments */
  110.     if (key == CPP_ENDIF)
  111.         sdir = REVERSE;
  112.     else
  113.         sdir = FORWARD;
  114.  
  115.     /* set up for scan */
  116.     if (sdir == REVERSE)
  117.         DOT.l = lback(DOT.l);
  118.     else
  119.         DOT.l = lforw(DOT.l);
  120.  
  121.     while (count > 0 && !is_header_line(DOT, curbp)) {
  122.         if (((that = match_complex(DOT.l)) != CPP_UNKNOWN)) {
  123.             int    done = FALSE;
  124.  
  125.             switch (that) {
  126.             case CPP_IF:
  127.                 if (sdir == FORWARD) {
  128.                     count++;
  129.                 } else {
  130.                     done = ((count-- == 1) &&
  131.                         (key != that));
  132.                     if (done)
  133.                         count = 0;
  134.                 }
  135.                 break;
  136.  
  137.             case CPP_ELIF:
  138.             case CPP_ELSE:
  139.                 done = ((sdir == FORWARD) && (count == 1));
  140.                 if (done)
  141.                     count = 0;
  142.                 break;
  143.  
  144.             case CPP_ENDIF:
  145.                 if (sdir == FORWARD) {
  146.                     done = (--count == 0);
  147.                 } else {
  148.                     count++;
  149.                 }
  150.             }
  151.  
  152.             if ((count <= 0) || done) {
  153.                 (void) firstnonwhite(FALSE,1);
  154.                 break;
  155.             }
  156.         }
  157.  
  158.         if (sdir == REVERSE)
  159.             DOT.l = lback(DOT.l);
  160.         else
  161.             DOT.l = lforw(DOT.l);
  162.  
  163.         if (is_header_line(DOT,curbp) || interrupted())
  164.             return FALSE;
  165.     }
  166.     if (count == 0) {
  167.         curwp->w_flag |= WFMOVE;
  168.         if (doingopcmd)
  169.             regionshape = FULLLINE;
  170.         return TRUE;
  171.     }
  172.     return FALSE;
  173. }
  174.  
  175. int
  176. is_user_fence(int ch, int *sdirp)
  177. {
  178.     char *fences = b_val_ptr(curbp,VAL_FENCES);
  179.     char *chp, och;
  180.     if (!ch)
  181.         return 0;
  182.     chp = strchr(fences, ch);
  183.     if (!chp)
  184.         return 0;
  185.     if ((chp - fences) & 1) {
  186.         /* look for the left fence */
  187.         och = chp[-1];
  188.         if (sdirp)
  189.             *sdirp = REVERSE;
  190.     } else {
  191.         /* look for the right fence */
  192.         och = chp[1];
  193.         if (sdirp)
  194.             *sdirp = FORWARD;
  195.     }
  196.     return och;
  197. }
  198.  
  199. static int
  200. simple_fence(int sdir, int ch, int ofence)
  201. {
  202.     int count = 1;    /* Assmue that we're sitting at one end of the fence */
  203.     int c;
  204.  
  205.     /* scan for fence */
  206.     while (InDirection(sdir) && !interrupted()) {
  207.         c = CurrentChar();
  208.         if (c == ch) {
  209.             ++count;
  210.         } else if (c == ofence) {
  211.             if (--count <= 0)
  212.                 break;
  213.         }
  214.     }
  215.  
  216.     /* if count is zero, we have a match, move the sucker */
  217.     if (count <= 0) {
  218.         if (!doingopcmd || doingsweep)
  219.             sweephack = TRUE;
  220.         else if (sdir == FORWARD)
  221.             forwchar(TRUE,1);
  222.         curwp->w_flag |= WFMOVE;
  223.         return TRUE;
  224.     }
  225.     return FALSE;
  226. }
  227.  
  228. static int
  229. comment_fence(int sdir)
  230. {
  231.     /* avoid overlapping match between begin/end patterns */
  232.     if (sdir == FORWARD) {
  233.         SIZE_T off = (SIZE_T)(DOT.o - S_COL(BlkBegin));
  234.         if (BlkEnd->mlen > off)
  235.             forwchar(TRUE, BlkEnd->mlen - off);
  236.     }
  237.  
  238.     scanboundry(FALSE,DOT,sdir);
  239.     if (scanner((sdir == FORWARD) ? BlkEnd : BlkBegin,
  240.             sdir, FALSE, (int *)0)) {
  241.         if (!doingopcmd || doingsweep)
  242.         {
  243.             sweephack = TRUE;
  244.             if (sdir == FORWARD && (BlkEnd->mlen > 1))
  245.                 forwchar(TRUE, BlkEnd->mlen - 1);
  246.         }
  247.         else if (sdir == FORWARD && (BlkEnd->mlen > 1))
  248.         {
  249.             forwchar(TRUE, BlkEnd->mlen - 0);
  250.         }
  251.         curwp->w_flag |= WFMOVE;
  252.         return TRUE;
  253.     }
  254.     return FALSE;
  255. }
  256.  
  257. static int
  258. getfence(
  259. int ch, /* fence type to match against */
  260. int sdir) /* direction to scan if we're not on a fence to begin with */
  261. {
  262.     MARK    oldpos;         /* original pointer */
  263.     register int ofence = 0;    /* open fence */
  264.     int s, i;
  265.     int key = CPP_UNKNOWN;
  266.     int fch;
  267.  
  268.     /* save the original cursor position */
  269.     oldpos = DOT;
  270.  
  271.     /* ch may have been passed, if being used internally */
  272.     if (ch < 0) {
  273.         if ((i = firstchar(DOT.l)) < 0)    /* offset of first nonblank */
  274.             return FALSE;        /* line is entirely blank */
  275.  
  276.         if (DOT.o <= i
  277.          && ((key = match_complex(DOT.l)) != CPP_UNKNOWN)) {
  278.             ch = COMPLEX_FENCE_CH;
  279.         } else if ((key = match_simple()) != BLK_UNKNOWN) {
  280.             ch = COMMENT_FENCE_CH;
  281.             sdir = (key == BLK_BEGIN) ? FORWARD : REVERSE;
  282.         } else if (sdir == FORWARD) {
  283.             /* get the current character */
  284.             if (oldpos.o < llength(oldpos.l)) {
  285.                 do {
  286.                     ch = char_at(oldpos);
  287.                 } while(!is_user_fence(ch, (int *)0) &&
  288.                     ++oldpos.o < llength(oldpos.l));
  289.             }
  290.             if (is_at_end_of_line(oldpos)) {
  291.                 return FALSE;
  292.             }
  293.         } else {
  294.             /* get the current character */
  295.             if (oldpos.o >= 0) {
  296.                 do {
  297.                     ch = char_at(oldpos);
  298.                 } while(!is_user_fence(ch, (int *)0) &&
  299.                     --oldpos.o >= 0);
  300.             }
  301.  
  302.             if (oldpos.o < 0) {
  303.                 return FALSE;
  304.             }
  305.         }
  306.  
  307.         /* we've at least found a fence -- move us that far */
  308.         DOT.o = oldpos.o;
  309.     }
  310.  
  311.     fch = ch;
  312.  
  313.     if (ch >= 0) {
  314.         ofence = is_user_fence(ch, &sdir);
  315.         if (ofence)
  316.             fch = PAIRED_FENCE_CH;
  317.     }
  318.  
  319.     /* setup proper matching fence */
  320.     if (fch >= 0) {
  321.         if ((key = match_complex(DOT.l)) == CPP_UNKNOWN
  322.          || (key = match_simple()) == BLK_UNKNOWN)
  323.             return(FALSE);
  324.     }
  325.  
  326.     /* ops are inclusive of the endpoint */
  327.     if (doingopcmd && sdir == REVERSE) {
  328.         forwchar(TRUE,1);
  329.         pre_op_dot = DOT;
  330.         backchar(TRUE,1);
  331.     }
  332.  
  333.     if (ok_CPP(key)) {  /* we're searching for a cpp keyword */
  334.         s = complex_fence(sdir, key);
  335.     } else if (ok_BLK(key)) {
  336.         s = comment_fence(sdir);
  337.     } else if (ch == '/') {
  338.         s = comment_fence(sdir);
  339.     } else {
  340.         s = simple_fence(sdir, ch, ofence);
  341.     }
  342.  
  343.     if (s == TRUE)
  344.         return TRUE;
  345.  
  346.     /* restore the current position */
  347.     DOT = oldpos;
  348.     return(FALSE);
  349. }
  350.  
  351. /*    the cursor is moved to a matching fence */
  352. int
  353. matchfence(int f, int n)
  354. {
  355.     int s = getfence(UNKNOWN_FENCE_CH, (!f || n > 0) ? FORWARD:REVERSE);
  356.     if (s == FALSE)
  357.         kbd_alarm();
  358.     return s;
  359. }
  360.  
  361. int
  362. matchfenceback(int f, int n)
  363. {
  364.     int s = getfence(UNKNOWN_FENCE_CH, (!f || n > 0) ? REVERSE:FORWARD);
  365.     if (s == FALSE)
  366.         kbd_alarm();
  367.     return s;
  368. }
  369.  
  370. /* get the indent of the line containing the matching brace/paren. */
  371. int
  372. fmatchindent(int c)
  373. {
  374.     int ind;
  375.  
  376.     MK = DOT;
  377.  
  378.     if (getfence(c,REVERSE) == FALSE) {
  379.         (void)gomark(FALSE,1);
  380.         return previndent((int *)0);
  381.     }
  382.  
  383.     ind = indentlen(DOT.l);
  384.  
  385.     (void)gomark(FALSE,1);
  386.  
  387.     return ind;
  388. }
  389.  
  390.  
  391.  
  392. /*    Close fences are matched against their partners, and if
  393.     on screen the cursor briefly lights there        */
  394. void
  395. fmatch(int rch)
  396. {
  397.     MARK    oldpos;     /* original position */
  398.     register LINE *toplp;    /* top line in current window */
  399.     register int count;    /* current fence level count */
  400.     register char c;    /* current character in scan */
  401.     int dir, lch;
  402.     int backcharfailed = FALSE;
  403.  
  404.     /* get the matching left-fence char, if it exists */
  405.     lch = is_user_fence(rch, &dir);
  406.     if (lch == 0 || dir != REVERSE)
  407.         return;
  408.  
  409.     /* first get the display update out there */
  410.     (void)update(FALSE);
  411.  
  412.     /* save the original cursor position */
  413.     oldpos = DOT;
  414.  
  415.     /* find the top line and set up for scan */
  416.     toplp = lback(curwp->w_line.l);
  417.     count = 1;
  418.     backchar(TRUE, 2);
  419.  
  420.     /* scan back until we find it, or reach past the top of the window */
  421.     while (count > 0 && DOT.l != toplp) {
  422.         c = CurrentChar();
  423.         if (c == rch)
  424.             ++count;
  425.         if (c == lch)
  426.             --count;
  427.         if (backchar(FALSE, 1) != TRUE) {
  428.             backcharfailed = TRUE;
  429.             break;
  430.         }
  431.     }
  432.  
  433.     /* if count is zero, we have a match, display the sucker */
  434.     if (count == 0) {
  435.         if (!backcharfailed)
  436.             forwchar(FALSE, 1);
  437.         if (update(SORTOFTRUE) == TRUE)
  438.         /* the idea is to leave the cursor there for about a
  439.             quarter of a second */
  440.             catnap(300, FALSE);
  441.     }
  442.  
  443.     /* restore the current position */
  444.     DOT = oldpos;
  445. }
  446.  
  447. #endif /* OPT_CFENCE */
  448.