aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuss Cox <rsc@swtch.com>2009-07-26 15:05:07 -0400
committerRuss Cox <rsc@swtch.com>2009-07-26 15:05:07 -0400
commite47d0a1e98d8a77f0196764d3f7e682715793e1d (patch)
tree2a9627a88bb136f914c44a8e6c1b2f292c18873e /src
parent6f4a41c68c39970dab1d0e09393a57b6cc3f55d6 (diff)
downloadplan9port-e47d0a1e98d8a77f0196764d3f7e682715793e1d.tar.gz
plan9port-e47d0a1e98d8a77f0196764d3f7e682715793e1d.tar.bz2
plan9port-e47d0a1e98d8a77f0196764d3f7e682715793e1d.zip
acme: angle bracket tag matching, for XML, HTML etc
http://codereview.appspot.com/98042
Diffstat (limited to 'src')
-rw-r--r--src/cmd/acme/dat.h1
-rw-r--r--src/cmd/acme/text.c110
2 files changed, 111 insertions, 0 deletions
diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
index 93af258d..8242da69 100644
--- 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
index f330d4b8..f1fc7329 100644
--- 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)
{