1 /* $OpenBSD: match.c,v 1.16 2009/06/04 02:23:37 kjell Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Limited parenthesis matching routines 7 * 8 * The hacks in this file implement automatic matching * of (), [], {}, and 9 * other characters. It would be better to have a full-blown syntax table, 10 * but there's enough overhead in the editor as it is. 11 */ 12 13 #include "def.h" 14 #include "key.h" 15 16 static int balance(void); 17 static void displaymatch(struct line *, int); 18 19 /* 20 * Balance table. When balance() encounters a character that is to be 21 * matched, it first searches this table for a balancing left-side character. 22 * If the character is not in the table, the character is balanced by itself. 23 */ 24 static struct balance { 25 char left, right; 26 } bal[] = { 27 { '(', ')' }, 28 { '[', ']' }, 29 { '{', '}' }, 30 { '<', '>' }, 31 { '\0', '\0' } 32 }; 33 34 /* 35 * Hack to show matching paren. Self-insert character, then show matching 36 * character, if any. Bound to "blink-and-insert". 37 */ 38 int 39 showmatch(int f, int n) 40 { 41 int i, s; 42 43 for (i = 0; i < n; i++) { 44 if ((s = selfinsert(FFRAND, 1)) != TRUE) 45 return (s); 46 /* unbalanced -- warn user */ 47 if (balance() != TRUE) 48 ttbeep(); 49 } 50 return (TRUE); 51 } 52 53 /* 54 * Search for and display a matching character. 55 * 56 * This routine does the real work of searching backward 57 * for a balancing character. If such a balancing character 58 * is found, it uses displaymatch() to display the match. 59 */ 60 static int 61 balance(void) 62 { 63 struct line *clp; 64 int cbo; 65 int c, i, depth; 66 int rbal, lbal; 67 68 rbal = key.k_chars[key.k_count - 1]; 69 70 /* See if there is a matching character -- default to the same */ 71 lbal = rbal; 72 for (i = 0; bal[i].right != '\0'; i++) 73 if (bal[i].right == rbal) { 74 lbal = bal[i].left; 75 break; 76 } 77 78 /* 79 * Move behind the inserted character. We are always guaranteed 80 * that there is at least one character on the line, since one was 81 * just self-inserted by blinkparen. 82 */ 83 clp = curwp->w_dotp; 84 cbo = curwp->w_doto - 1; 85 86 /* init nesting depth */ 87 depth = 0; 88 89 for (;;) { 90 if (cbo == 0) { 91 clp = lback(clp); /* beginning of line */ 92 if (clp == curbp->b_headp) 93 return (FALSE); 94 cbo = llength(clp) + 1; 95 } 96 if (--cbo == llength(clp)) 97 c = '\n'; /* end of line */ 98 else 99 c = lgetc(clp, cbo); /* somewhere in middle */ 100 101 /* 102 * Check for a matching character. If still in a nested 103 * level, pop out of it and continue search. This check 104 * is done before the nesting check so single-character 105 * matches will work too. 106 */ 107 if (c == lbal) { 108 if (depth == 0) { 109 displaymatch(clp, cbo); 110 return (TRUE); 111 } else 112 depth--; 113 } 114 /* Check for another level of nesting. */ 115 if (c == rbal) 116 depth++; 117 } 118 /* NOTREACHED */ 119 } 120 121 /* 122 * Display matching character. Matching characters that are not in the 123 * current window are displayed in the echo line. If in the current window, 124 * move dot to the matching character, sit there a while, then move back. 125 */ 126 static void 127 displaymatch(struct line *clp, int cbo) 128 { 129 struct line *tlp; 130 int tbo; 131 int cp; 132 int bufo; 133 int c; 134 int inwindow; 135 char buf[NLINE]; 136 137 /* 138 * Figure out if matching char is in current window by 139 * searching from the top of the window to dot. 140 */ 141 inwindow = FALSE; 142 for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp); 143 tlp = lforw(tlp)) 144 if (tlp == clp) 145 inwindow = TRUE; 146 147 if (inwindow == TRUE) { 148 tlp = curwp->w_dotp; /* save current position */ 149 tbo = curwp->w_doto; 150 151 curwp->w_dotp = clp; /* move to new position */ 152 curwp->w_doto = cbo; 153 curwp->w_rflag |= WFMOVE; 154 155 update(); /* show match */ 156 ttwait(1000); /* wait for key or 1 second */ 157 158 curwp->w_dotp = tlp; /* return to old position */ 159 curwp->w_doto = tbo; 160 curwp->w_rflag |= WFMOVE; 161 update(); 162 } else { 163 /* match is not in this window, so display line in echo area */ 164 bufo = 0; 165 for (cp = 0; cp < llength(clp); cp++) { 166 c = lgetc(clp, cp); 167 if (c != '\t' 168 #ifdef NOTAB 169 || (curbp->b_flag & BFNOTAB) 170 #endif 171 ) 172 if (ISCTRL(c)) { 173 buf[bufo++] = '^'; 174 buf[bufo++] = CCHR(c); 175 } else 176 buf[bufo++] = c; 177 else 178 do { 179 buf[bufo++] = ' '; 180 } while (bufo & 7); 181 } 182 buf[bufo++] = '\0'; 183 ewprintf("Matches %s", buf); 184 } 185 } 186