plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

commit e47d0a1e98d8a77f0196764d3f7e682715793e1d
parent 6f4a41c68c39970dab1d0e09393a57b6cc3f55d6
Author: Russ Cox <rsc@swtch.com>
Date:   Sun, 26 Jul 2009 15:05:07 -0400

acme: angle bracket tag matching, for XML, HTML etc

http://codereview.appspot.com/98042

Diffstat:
Msrc/cmd/acme/dat.h | 1+
Msrc/cmd/acme/text.c | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+), 0 deletions(-)

diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h @@ -198,6 +198,7 @@ struct Text uint textbacknl(Text*, uint, uint); uint textbsinsert(Text*, uint, Rune*, uint, int, int*); int textbswidth(Text*, Rune); +int textclickhtmlmatch(Text*, uint*, uint*); int textclickmatch(Text*, int, int, int, uint*); void textclose(Text*); void textcolumnate(Text*, Dirlist**, int); diff --git a/src/cmd/acme/text.c b/src/cmd/acme/text.c @@ -1381,6 +1381,10 @@ textdoubleclick(Text *t, uint *q0, uint *q1) return; } } + + if(textclickhtmlmatch(t, q0, q1)) + return; + /* try filling out word to right */ while(*q1<t->file->b.nc && isalnum(textreadc(t, *q1))) (*q1)++; @@ -1417,6 +1421,112 @@ textclickmatch(Text *t, int cl, int cr, int dir, uint *q) return cl=='\n' && nest==1; } +// Is the text starting at location q an html tag? +// Return 1 for <a>, -1 for </a>, 0 for no tag or <a />. +// Set *q1, if non-nil, to the location after the tag. +static int +ishtmlstart(Text *t, uint q, uint *q1) +{ + int c, c1, c2; + + if(q+2 > t->file->b.nc) + return 0; + if(textreadc(t, q++) != '<') + return 0; + c = textreadc(t, q++); + c1 = c; + c2 = c; + while(c != '>') { + if(q >= t->file->b.nc) + return 0; + c2 = c; + c = textreadc(t, q++); + } + if(q1) + *q1 = q; + if(c1 == '/') // closing tag + return -1; + if(c2 == '/' || c2 == '!') // open + close tag or comment + return 0; + return 1; +} + +// Is the text ending at location q an html tag? +// Return 1 for <a>, -1 for </a>, 0 for no tag or <a />. +// Set *q0, if non-nil, to the start of the tag. +static int +ishtmlend(Text *t, uint q, uint *q0) +{ + int c, c1, c2; + + if(q < 2) + return 0; + if(textreadc(t, --q) != '>') + return 0; + c = textreadc(t, --q); + c1 = c; + c2 = c; + while(c != '<') { + if(q == 0) + return 0; + c1 = c; + c = textreadc(t, --q); + } + if(q0) + *q0 = q; + if(c1 == '/') // closing tag + return -1; + if(c2 == '/' || c2 == '!') // open + close tag or comment + return 0; + return 1; +} + +int +textclickhtmlmatch(Text *t, uint *q0, uint *q1) +{ + int depth, n; + uint q, nq; + + q = *q0; + // after opening tag? scan forward for closing tag + if(ishtmlend(t, q, nil) == 1) { + depth = 1; + while(q < t->file->b.nc) { + n = ishtmlstart(t, q, &nq); + if(n != 0) { + depth += n; + if(depth == 0) { + *q1 = q; + return 1; + } + q = nq; + continue; + } + q++; + } + } + + // before closing tag? scan backward for opening tag + if(ishtmlstart(t, q, nil) == -1) { + depth = -1; + while(q > 0) { + n = ishtmlend(t, q, &nq); + if(n != 0) { + depth += n; + if(depth == 0) { + *q0 = q; + return 1; + } + q = nq; + continue; + } + q--; + } + } + + return 0; +} + uint textbacknl(Text *t, uint p, uint n) {