love to code .net

blogging mostly about one of my favorite subjects, programming

Month List


Proper line numbering with BlogEngine.NET and source code without line feeds

I wanted to use Windows Live Writer to create my posts.  Before going live, I decided to test out WLW with a few posts.  During the tests I noticed that code was being shown with multiple lines, but only one line number.  After looking at the XML posts and code, I noticed that the source code formatter expects each line to be separated by a line feed and that WLW separates lines by a <br/> with no line feed.  So my simple solution was to modify the code sections before they get formatted to insure that there is a line feed after each <br/>.

Here's the code along with the fix I inserted on lines 14-16.  I also had to change line 17 to use the updated string stored in source and not the StringBuilder instance sb.

1: //does the formatting job
2:         private string FormatCode(string source, bool lineNumbers,
3:             bool alternate, bool embedStyleSheet, bool subCode)
4:         {
5:             //replace special characters
6:             StringBuilder sb = new StringBuilder(source);
7:             if(!subCode)
8:             {
9:                 sb.Replace("&", "&amp;");
10:                 sb.Replace("<", "&lt;");
11:                 sb.Replace(">", "&gt;");
12:                 sb.Replace("\t", string.Empty.PadRight(_tabSpaces));
13:             }
14:             //Allow for <br/>s not just line feeds.
15:             Regex r = new Regex("&lt;br[ \\t]*/*&gt;(?=[ \\t]*(?:[^\\r\\n]))");
16:             source = r.Replace(sb.ToString(), "&lt;br /&gt;\n");
17:             //color the code
18:             source = codeRegex.Replace(source, new MatchEvaluator(this.MatchEval));
19:             sb = new StringBuilder();
20:       //if (embedStyleSheet)
21:       //{
22:       //  sb.Append("<style type=\"text/css\">\n");
23:       //  sb.Append(GetCssString());
24:       //  sb.Append("</style>\n");
25:       //}
26:             if (lineNumbers || alternate) //we have to process the code line by line
27:             {
28:                 if(!subCode)
29:                     sb.Append("<div class=\"code\">\n");
30:                 StringReader reader = new StringReader(source);
31:                 int i = 0;
32:                 string spaces = "    ";
33:                 int order;
34:                 string line;
35:                 while ((line = reader.ReadLine()) != null)
36:                 {
37:                     i++;
38:                     if (alternate && ((i % 2) == 1))
39:                     {
40:                         sb.Append("<div class=\"alt\">");
41:                     }
42:                     else
43:                     {
44:                         sb.Append("<div>");
45:                     }
46:                     if(lineNumbers)
47:                     {
48:                         order = (int)Math.Log10(i);
49:                         sb.Append("<span class=\"lnum\">"
50:                             + spaces.Substring(0, 3 - order) + i.ToString()
51:                             + ":  </span>");
52:                     }
53:                     if(line.Length == 0)
54:                         sb.Append("&nbsp;");
55:                     else
56:                         sb.Append(line);
57:                     sb.Append("</div>\n");
58:                 }
59:                 reader.Close();
60:                 if(!subCode)
61:                     sb.Append("</div>");
62:             }
63:             else
64:             {
65:                 //have to use a <pre> because IE below ver 6 does not understand
66:                 //the "white-space: pre" CSS value
67:                 if(!subCode)
68:                     sb.Append("<div class=\"code\">\n");
69:                 sb.Append(source);
70:                 if(!subCode)
71:                     sb.Append("</div>");
72:             }
73:             return sb.ToString();
74:         }

Here's an explanation of the regular expression on line 15, for those not familiar with the regular expression syntax.  The first half of the expression, &lt;br[ \\t]*/*&gt;, looks for a <br>, where there can be any amount of white space and possibly a / to close the tag.  In a few tests the tag was not closed, so I made closing the tag optional.  The second half of the expression, (?=[ \\t]*(?:[^\\r\\n]|&)), prevents inserting line feeds for tags that already have one.  It ignores any white space between the end of the tag and the line feed.

As promised, here's the updated source file as promised in my last post.  The code in this post is released under the Microsoft Reciprocal License, which is the license BlogEngine.NET uses.

kick it on DotNetKicks.com

Comments are closed