Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/match.c
- This file was added.
| /* $OpenBSD: match.c,v 1.22 2021/03/01 10:51:14 lum Exp $ */ | |||||
| /* This file is in the public domain. */ | |||||
| /* | |||||
| * Limited parenthesis matching routines | |||||
| * | |||||
| * The hacks in this file implement automatic matching * of (), [], {}, and | |||||
| * other characters. It would be better to have a full-blown syntax table, | |||||
| * but there's enough overhead in the editor as it is. | |||||
| */ | |||||
| #include <sys/queue.h> | |||||
| #include <signal.h> | |||||
| #include <stdio.h> | |||||
| #include "def.h" | |||||
| #include "key.h" | |||||
| static int balance(void); | |||||
| static void displaymatch(struct line *, int); | |||||
| /* | |||||
| * Balance table. When balance() encounters a character that is to be | |||||
| * matched, it first searches this table for a balancing left-side character. | |||||
| * If the character is not in the table, the character is balanced by itself. | |||||
| */ | |||||
| static struct balance { | |||||
| char left, right; | |||||
| } bal[] = { | |||||
| { '(', ')' }, | |||||
| { '[', ']' }, | |||||
| { '{', '}' }, | |||||
| { '<', '>' }, | |||||
| { '\0', '\0' } | |||||
| }; | |||||
| /* | |||||
| * Hack to show matching paren. Self-insert character, then show matching | |||||
| * character, if any. Bound to "blink-and-insert". Used in the mg startup | |||||
| * file to amend the default cursor behaviour of a key press, e.g: | |||||
| * global-set-key "%" blink-and-insert | |||||
| */ | |||||
| int | |||||
| showmatch(int f, int n) | |||||
| { | |||||
| int i, s; | |||||
| for (i = 0; i < n; i++) { | |||||
| if ((s = selfinsert(FFRAND, 1)) != TRUE) | |||||
| return (s); | |||||
| /* unbalanced -- warn user */ | |||||
| if (balance() != TRUE) | |||||
| dobeep(); | |||||
| } | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Search for and display a matching character. | |||||
| * | |||||
| * This routine does the real work of searching backward | |||||
| * for a balancing character. If such a balancing character | |||||
| * is found, it uses displaymatch() to display the match. | |||||
| */ | |||||
| static int | |||||
| balance(void) | |||||
| { | |||||
| struct line *clp; | |||||
| int cbo; | |||||
| int c, i, depth; | |||||
| int rbal, lbal; | |||||
| rbal = key.k_chars[key.k_count - 1]; | |||||
| /* See if there is a matching character -- default to the same */ | |||||
| lbal = rbal; | |||||
| for (i = 0; bal[i].right != '\0'; i++) | |||||
| if (bal[i].right == rbal) { | |||||
| lbal = bal[i].left; | |||||
| break; | |||||
| } | |||||
| /* | |||||
| * Move behind the inserted character. We are always guaranteed | |||||
| * that there is at least one character on the line. | |||||
| */ | |||||
| clp = curwp->w_dotp; | |||||
| cbo = curwp->w_doto - 1; | |||||
| /* init nesting depth */ | |||||
| depth = 0; | |||||
| for (;;) { | |||||
| if (cbo == 0) { | |||||
| clp = lback(clp); /* beginning of line */ | |||||
| if (clp == curbp->b_headp) | |||||
| return (FALSE); | |||||
| cbo = llength(clp) + 1; | |||||
| } | |||||
| if (--cbo == llength(clp)) | |||||
| c = *curbp->b_nlchr; /* end of line */ | |||||
| else | |||||
| c = lgetc(clp, cbo); /* somewhere in middle */ | |||||
| /* | |||||
| * Check for a matching character. If still in a nested | |||||
| * level, pop out of it and continue search. This check | |||||
| * is done before the nesting check so single-character | |||||
| * matches will work too. | |||||
| */ | |||||
| if (c == lbal) { | |||||
| if (depth == 0) { | |||||
| displaymatch(clp, cbo); | |||||
| return (TRUE); | |||||
| } else | |||||
| depth--; | |||||
| } | |||||
| /* Check for another level of nesting. */ | |||||
| if (c == rbal) | |||||
| depth++; | |||||
| } | |||||
| /* NOTREACHED */ | |||||
| } | |||||
| /* | |||||
| * Display matching character. Matching characters that are not in the | |||||
| * current window are displayed in the echo line. If in the current window, | |||||
| * move dot to the matching character, sit there a while, then move back. | |||||
| */ | |||||
| static void | |||||
| displaymatch(struct line *clp, int cbo) | |||||
| { | |||||
| struct line *tlp; | |||||
| int tbo; | |||||
| int cp; | |||||
| int bufo; | |||||
| int c; | |||||
| int inwindow; | |||||
| char buf[NLINE]; | |||||
| /* | |||||
| * Figure out if matching char is in current window by | |||||
| * searching from the top of the window to dot. | |||||
| */ | |||||
| inwindow = FALSE; | |||||
| for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp); | |||||
| tlp = lforw(tlp)) | |||||
| if (tlp == clp) | |||||
| inwindow = TRUE; | |||||
| if (inwindow == TRUE) { | |||||
| tlp = curwp->w_dotp; /* save current position */ | |||||
| tbo = curwp->w_doto; | |||||
| curwp->w_dotp = clp; /* move to new position */ | |||||
| curwp->w_doto = cbo; | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| update(CMODE); /* show match */ | |||||
| ttwait(1000); /* wait for key or 1 second */ | |||||
| curwp->w_dotp = tlp; /* return to old position */ | |||||
| curwp->w_doto = tbo; | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| update(CMODE); | |||||
| } else { | |||||
| /* match is not in this window, so display line in echo area */ | |||||
| bufo = 0; | |||||
| for (cp = 0; cp < llength(clp); cp++) { | |||||
| c = lgetc(clp, cp); | |||||
| if (c != '\t' | |||||
| #ifdef NOTAB | |||||
| || (curbp->b_flag & BFNOTAB) | |||||
| #endif | |||||
| ) | |||||
| if (ISCTRL(c)) { | |||||
| buf[bufo++] = '^'; | |||||
| buf[bufo++] = CCHR(c); | |||||
| } else | |||||
| buf[bufo++] = c; | |||||
| else | |||||
| do { | |||||
| buf[bufo++] = ' '; | |||||
| } while (bufo & 7); | |||||
| } | |||||
| buf[bufo++] = '\0'; | |||||
| ewprintf("Matches %s", buf); | |||||
| } | |||||
| } | |||||