tacme: angle bracket tag matching, for XML, HTML etc - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit e47d0a1e98d8a77f0196764d3f7e682715793e1d
 (DIR) parent 6f4a41c68c39970dab1d0e09393a57b6cc3f55d6
 (HTM) 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:
         M src/cmd/acme/dat.h                  |       1 +
         M src/cmd/acme/text.c                 |     110 +++++++++++++++++++++++++++++++
       
       2 files changed, 111 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
       t@@ -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);
 (DIR) diff --git a/src/cmd/acme/text.c b/src/cmd/acme/text.c
       t@@ -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)++;
       t@@ -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)
        {