File: [local] / src / usr.bin / mg / match.c (download)
Revision 1.25, Fri Apr 21 13:39:37 2023 UTC (13 months ago) by op
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, HEAD Changes since 1.24: +4 -3 lines
mg: allow to change the tab width
This makes the tab width customizable per-buffer. The new function
`set-tab-width' changes it for the current buffer or the default value
for new buffers if called with a prefix argument (or from the startup
file.)
The default tab width is still 8 column.
Together with the newly resurrected no-tab-mode, allows to use mg for a
variety of programming languages and coding styles.
Note that it's not possible to call set-tab-width with auto-execute in
the startup file due to limitations in how auto-execute and the parser
work.
ok tb@
|
/* $OpenBSD: match.c,v 1.25 2023/04/21 13:39:37 op 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 col;
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++) {
if (bufo >= sizeof(buf) - 1)
break;
c = lgetc(clp, cp);
if (c != '\t') {
if (ISCTRL(c)) {
if (bufo >= sizeof(buf) - 3)
break;
buf[bufo++] = '^';
buf[bufo++] = CCHR(c);
} else
buf[bufo++] = c;
} else {
col = ntabstop(bufo, curbp->b_tabw);
while (bufo < col && bufo < sizeof(buf) - 1)
buf[bufo++] = ' ';
}
}
buf[bufo++] = '\0';
ewprintf("Matches %s", buf);
}
}