xref: /openbsd-src/usr.bin/mg/match.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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