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