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