Highlight C# source code

Επαιζα πριν λίγο καιρό με την php και χρησιμοποιούσα την show_source για να δείχνω χρωματισμένο τον κώδικα. Κάποια στιγμή που προσπάθησα να μεταφράσω τον κώδικα από php σε C# για να δοκιμάσω την ταχύτητα της αντίστοιχης aspx σελίδας, χρειάστηκα πάλι την show_source, αλλά όσο έψαξα δεν βρήκα να δίνει κάτι αντίστοιχο το .net framework.

Ξεκίνησα την αναζήτηση στο internet (το έμαθα το μάθημα μου), αφού λέω τόσα χρόνια που ασχολείται ο κόσμος, δεν μπορεί κάποιος το έχει χρειαστεί και θα το έχει φτιάξει ήδη. Αν και δεν βρήκα κάτι έτοιμο ακριβώς όπως το ήθελα, βρήκα κάτι πολύ κοντινό. Ηταν ένα άρθρο για C++ highlighter στο codeproject. Απλή δουλειά πλέον, αφού ήταν ήδη γραμμένο σε C#. Το μόνο που έμενε ήταν να αλλάξουν τα keywords της C++ σε αυτά της C#. Αφού άρχισα να βάζω χέρι στον κώδικα, με έπιασε λίγο το μικρόβιο και δεν σταμάτησα στην απλή αντικατάσταση, αλλά είπα αντί για το font tag της html, να βάλω css style, ώστε να είναι πιο εύκολη η παραμετροποίηση των χρωμάτων…

Ετσι στη σελίδα μου το μόνο που θα χρειαζόταν να κάνω είναι να γράψω τον παρακάτω κώδικα για να εμφανίζω τον source κώδικα όπου χρειαζόμουν.

<% this.ShowFile("Default.aspx.cs"); %>

Και στο codebehind αρχείο (ή σε κάποια άλλη library) θα μπορούσε να μπεί η function

protected void ShowFile(string filename)
{
    string path = Request.PhysicalApplicationPath;
    System.IO.StreamReader rd = new System.IO.StreamReader( path + filename, System.Text.Encoding.UTF8, true );
    do {
        Response.Write( highlightCS.SourceCode( rd.ReadToEnd() ) );
    } while ( !rd.EndOfStream );
    rd.Close();
    rd.Dispose();
}

Κατεβάστε τον κώδικα για τον highligher, η δείτε τον παρακάτω:

/*
 Initial source code from http://www.codeproject.com/KB/recipes/CppHighLiter.aspx
 */
using System;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

using System.Collections.Specialized;
using System.Threading;

namespace utils.web
{
    class Tokenizer
    {
        #region Initialization
        static object mutex = new object();

        protected static bool hasInitCollections = false;
        protected static StringCollection colPreProcessor = new StringCollection();
        protected static StringCollection colKeyword = new StringCollection();

        [STAThread]
        protected static void InitCollections()
        {
            lock (mutex)
            {
                if (hasInitCollections == false)
                {
                    #region C# preprocessors
                    colKeyword.Add("if");
                    colKeyword.Add("else");
                    colKeyword.Add("elif");
                    colKeyword.Add("endif");
                    colKeyword.Add("define");
                    colKeyword.Add("undef");
                    colKeyword.Add("warning");
                    colKeyword.Add("error");
                    colKeyword.Add("line");
                    colKeyword.Add("region");
                    colKeyword.Add("endregion");
                    #endregion C# preprocessors

                    #region C# keywords
                    colKeyword.Add("abstract");
                    colKeyword.Add("as");
                    colKeyword.Add("attributeusage");
                    colKeyword.Add("base");
                    colKeyword.Add("bool");
                    colKeyword.Add("break");
                    colKeyword.Add("byte");
                    colKeyword.Add("case");
                    colKeyword.Add("catch");
                    colKeyword.Add("char");
                    colKeyword.Add("checked");
                    colKeyword.Add("class");
                    colKeyword.Add("conditional");
                    colKeyword.Add("const");
                    colKeyword.Add("continue");
                    colKeyword.Add("decimal");
                    colKeyword.Add("default");
                    colKeyword.Add("delegate");
                    colKeyword.Add("do");
                    colKeyword.Add("double");
                    colKeyword.Add("else");
                    colKeyword.Add("enum");
                    colKeyword.Add("event");
                    colKeyword.Add("explicit");
                    colKeyword.Add("extern");
                    colKeyword.Add("false");
                    colKeyword.Add("finally");
                    colKeyword.Add("fixed");
                    colKeyword.Add("float");
                    colKeyword.Add("for");
                    colKeyword.Add("foreach");
                    colKeyword.Add("get");
                    colKeyword.Add("goto");
                    colKeyword.Add("if");
                    colKeyword.Add("implicit");
                    colKeyword.Add("in");
                    colKeyword.Add("int");
                    colKeyword.Add("interface");
                    colKeyword.Add("internal");
                    colKeyword.Add("is");
                    colKeyword.Add("lock");
                    colKeyword.Add("long");
                    colKeyword.Add("namespace");
                    colKeyword.Add("new");
                    colKeyword.Add("null");
                    colKeyword.Add("object");
                    colKeyword.Add("obsolete");
                    colKeyword.Add("operator");
                    colKeyword.Add("out");
                    colKeyword.Add("override");
                    colKeyword.Add("params");
                    colKeyword.Add("partial");
                    colKeyword.Add("private");
                    colKeyword.Add("protected");
                    colKeyword.Add("public");
                    colKeyword.Add("readonly");
                    colKeyword.Add("ref");
                    colKeyword.Add("return");
                    colKeyword.Add("sbyte");
                    colKeyword.Add("sealed");
                    colKeyword.Add("short");
                    colKeyword.Add("sizeof");
                    colKeyword.Add("stackalloc");
                    colKeyword.Add("static");
                    colKeyword.Add("set");
                    colKeyword.Add("string");
                    colKeyword.Add("struct");
                    colKeyword.Add("switch");
                    colKeyword.Add("this");
                    colKeyword.Add("throw");
                    colKeyword.Add("try");
                    colKeyword.Add("typeof");
                    colKeyword.Add("uint");
                    colKeyword.Add("ulong");
                    colKeyword.Add("unchecked");
                    colKeyword.Add("unsafe");
                    colKeyword.Add("ushort");
                    colKeyword.Add("using");
                    colKeyword.Add("value");
                    colKeyword.Add("virtual");
                    colKeyword.Add("void");
                    colKeyword.Add("volatile");
                    colKeyword.Add("while");
                    colKeyword.Add("true");
                    #endregion C# keywords

                    hasInitCollections = true;
                }
            }
        }
        #endregion Initialization

        public enum TokenType { code, comment, cstring, keyword, pp, number, seperator, none };
        private StringBuilder token_val = new StringBuilder();
        private TokenType token_type = TokenType.none;

        public Tokenizer()
        {
            InitCollections();
        }

        public TokenType Type
        {
            get
            {
                return token_type;
            }

            set
            {
                token_type = value;
            }
        }

        public string Value
        {
            get
            {
                return token_val.ToString();
            }
        }

        private bool IsTokenPreProcessor(string t_val)
        {
            return colPreProcessor.Contains(t_val);
        }

        private bool IsTokenSeperator( string t_val )
        {
            return ("();-+=*/!{}[]<>,&|^?:%".IndexOf( t_val ) >= 0);
        }

        private bool IsTokenKeyword(string t_val)
        {
            return colKeyword.Contains(t_val);
        }

        public bool ParseCode( StringReader orig_code_stream )
        {
            int iCharVal = orig_code_stream.Peek();
            if ( iCharVal != -1 )
            {
                orig_code_stream.Read();
                char c = Convert.ToChar( iCharVal );
                switch ( c )
                {
                    case '/':
                        iCharVal = orig_code_stream.Peek();
                        if ( iCharVal == -1 )
                            return false;

                        c = Convert.ToChar( iCharVal );
                        if ( c == '*' )
                        {
                            orig_code_stream.Read();

                            token_val.Append( "/*" );
                            this.token_type = TokenType.comment;

                            iCharVal = orig_code_stream.Peek();
                            while ( iCharVal != -1 )
                            {
                                orig_code_stream.Read();
                                c = Convert.ToChar( iCharVal );
                                if ( c == '/' )
                                {
                                    if ( token_val.Length > 2
                                    && token_val[token_val.Length-1] == '*' )
                                    {
                                        token_val.Append( "/" );
                                        return true;
                                    }
                                }
                                else
                                {
                                    token_val.Append( c );
                                }

                                iCharVal = orig_code_stream.Peek();
                            }
                        }
                        else if ( c == '/' )
                        {
                            orig_code_stream.Read();

                            token_val.Append( "//" );
                            this.token_type = TokenType.comment;

                            iCharVal = orig_code_stream.Peek();
                            while ( iCharVal != -1 && (c = Convert.ToChar( iCharVal )) != 'n' )
                            {
                                orig_code_stream.Read();
                                token_val.Append( c );
                                iCharVal = orig_code_stream.Peek();
                            }

                            if ( c == 'n' )
                            {
                                orig_code_stream.Read();
                                token_val.Append( c );
                            }
                            return true;
                        }
                        token_val.Append( "/" );
                        if ( IsTokenSeperator( token_val.ToString() ) )
                        {
                            this.token_type = TokenType.seperator;
                            return true;
                        }
                        return true;

                    case '#':
                        this.token_val.Append( '#' );

                        iCharVal = orig_code_stream.Peek();
                        if ( iCharVal == -1 )
                            return false;

                        c = Convert.ToChar( iCharVal );
                        while ( c == ' ' || c == 'r' || c == 'n' || c == 't' )
                        {
                            this.token_val.Append( c );
                            orig_code_stream.Read();
                            iCharVal = orig_code_stream.Peek();
                            if ( iCharVal == -1 )
                                return false;
                            c = Convert.ToChar( iCharVal );
                        }

                        while ( Char.IsLetter( c ) && Char.IsLower( c ) )
                        {
                            this.token_val.Append( c );
                            orig_code_stream.Read();
                            iCharVal = orig_code_stream.Peek();
                            if ( iCharVal == -1 )
                                break;
                            c = Convert.ToChar( iCharVal );
                        }

                        if ( IsTokenPreProcessor( this.token_val.ToString() ) )
                        {
                            this.token_type = TokenType.pp;
                            return true;
                        }
                        return false;
                    case ''':
                    case '"':
                        {
                            char q = c;
                            token_val.Append( q );
                            while ( true )
                            {
                                iCharVal = orig_code_stream.Peek();
                                if ( iCharVal == -1 )
                                    return false;

                                c = Convert.ToChar( iCharVal );
                                if ( c == q )
                                {
                                    if ( token_val.Length >= 2 )
                                    {
                                        if ( !(token_val[token_val.Length - 1] == '\' &&
                                    token_val[token_val.Length - 2] != '\') )
                                        {
                                            token_val.Append( q );
                                            orig_code_stream.Read();
                                            this.token_type = TokenType.cstring;
                                            return true;
                                        }
                                    }
                                    else
                                    {
                                        token_val.Append( q );
                                        orig_code_stream.Read();
                                        this.token_type = TokenType.cstring;
                                        return true;
                                    }
                                }
                                token_val.Append( c );
                                orig_code_stream.Read();
                            }
                        }
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'e':
                    case 'f':
                    case 'g':
                    case 'i':
                    case 'l':
                    case 'm':
                    case 'n':
                    case 'o':
                    case 'p':
                    case 'r':
                    case 's':
                    case 't':
                    case 'u':
                    case 'v':
                    case 'w':
                    case 'x':
                        token_val.Append( c );
                        iCharVal = orig_code_stream.Peek();
                        if ( iCharVal == -1 )
                            return false;

                        c = Convert.ToChar( iCharVal );
                        while ( Char.IsLetter( c ) || Char.IsDigit( c ) || c == '_' )
                        {
                            token_val.Append( c );
                            orig_code_stream.Read();
                            iCharVal = orig_code_stream.Peek();
                            if ( iCharVal == -1 )
                                return false;

                            c = Convert.ToChar( iCharVal );
                        }

                        if ( IsTokenKeyword( token_val.ToString() ) )
                        {
                            this.token_type = TokenType.keyword;
                            return true;
                        }
                        if ( IsTokenSeperator( token_val.ToString() ) )
                        {
                            this.token_type = TokenType.seperator;
                            return true;
                        }
                        //else
                        {
                            this.token_type = TokenType.code;
                            return true;
                        }
                    //return false;
                    default:
                        token_val.Append( c );
                        iCharVal = orig_code_stream.Peek();
                        if ( iCharVal == -1 )
                            return false;

                        if ( IsTokenSeperator( token_val.ToString() ) )
                        {
                            this.token_type = TokenType.seperator;
                            return true;
                        }

                        c = Convert.ToChar( iCharVal );
                        while ( c != '/' && c != '#' && !(Char.IsLetter( c ) && Char.IsLower( c ))
                        && c != ''' && c != '"' && !IsTokenSeperator( c.ToString() ) )
                        {
                            token_val.Append( c );
                            orig_code_stream.Read();
                            iCharVal = orig_code_stream.Peek();
                            if ( iCharVal == -1 )
                            {
                                if ( token_val.Length > 0 )
                                {
                                    this.token_type = TokenType.code;
                                    return true;
                                }
                                else
                                    return false;
                            }
                            c = Convert.ToChar( iCharVal );
                        }

                        if ( IsTokenSeperator( token_val.ToString() ) )
                        {
                            this.token_type = TokenType.seperator;
                            return true;
                        }

                        this.token_type = TokenType.code;
                        return true;

                }

            }

            return false;
        }
    }

    public class highlightCS
    {
        public static string ChangeCharToHtml( string orig_val )
        {
            string retVal = orig_val;
            retVal = Regex.Replace( retVal, "&", "&", RegexOptions.IgnoreCase );
            retVal = Regex.Replace( retVal, "<", "<", RegexOptions.IgnoreCase );             retVal = Regex.Replace( retVal, ">", ">", RegexOptions.IgnoreCase );
            retVal = Regex.Replace( retVal, """, """, RegexOptions.IgnoreCase );
            retVal = Regex.Replace( retVal, "t", "    ", RegexOptions.IgnoreCase );
            retVal = Regex.Replace( retVal, " ", " ", RegexOptions.IgnoreCase );
            retVal = Regex.Replace( retVal, "r", "", RegexOptions.IgnoreCase );
            retVal = Regex.Replace( retVal, "n", "
", RegexOptions.IgnoreCase );

            return retVal;
        }

        protected static string style =
            "";

        public static string StyleSheet
        {
            get
            {
                return style;
            }
        }

        public static string SourceCode( string orig_line_val )
        {
            StringReader strm = new StringReader( orig_line_val );
            StringBuilder bld = new StringBuilder();

            while ( true )
            {
                Tokenizer tok = new Tokenizer();
                if ( tok.ParseCode( strm ) )
                {
                    string outputVal = ChangeCharToHtml( tok.Value );
                    switch ( tok.Type )
                    {
                        case Tokenizer.TokenType.comment:
                            bld.Append( "" );
                            bld.Append( outputVal );
                            bld.Append( "" );
                            break;
                        case Tokenizer.TokenType.cstring:
                            bld.Append( "" );
                            bld.Append( outputVal );
                            bld.Append( "" );
                            break;
                        case Tokenizer.TokenType.keyword:
                            bld.Append( "" );
                            bld.Append( outputVal );
                            bld.Append( "" );
                            break;
                        case Tokenizer.TokenType.pp:
                            bld.Append( "" );
                            bld.Append( outputVal );
                            bld.Append( "" );
                            break;
                        case Tokenizer.TokenType.seperator:
                            bld.Append( "" );
                            bld.Append( outputVal );
                            bld.Append( "" );
                            break;
                        default:
                            bld.Append( outputVal );
                            break;
                    }
                }
                else
                    break;
            }

            return bld.ToString();
        }
    }
}

Comments are closed.