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