1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using Newtonsoft.Json.Linq;
using SourceBrowser.Generator.Model;
using SourceBrowser.Generator.Model.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace SourceBrowser.Generator.Transformers
{
    /// <summary>
    /// Converts a WorkspaceModel into HTML
    /// </summary>
    public class HtmlTransformer : AbstractWorkspaceVisitor
    {
        private string _savePath;
        private Dictionary<string, Token> _tokenLookup;
        public HtmlTransformer(Dictionary<string, Token> tokenLookup, string savePath)
        {
            _tokenLookup = tokenLookup;
            _savePath = savePath;
        }
        protected override void VisitDocument(DocumentModel documentModel)
        {
            var documentSavePath = Path.Combine(_savePath, documentModel.RelativePath);
            Directory.CreateDirectory(Path.GetDirectoryName(documentSavePath));

            using (var sw = new StreamWriter(documentSavePath))
            {
                sw.WriteLine("<table cellpadding='0' cellspacing='0'>");
                sw.WriteLine("<tbody>");
                sw.WriteLine("<tr>");
                sw.WriteLine("<td valign='top' style='min-diwth:65px'>");
                sw.WriteLine("<pre id='line-numbers'>");

                //Create the sidebar with line numbers
                this.createLineNumberHtml(sw, documentModel.NumberOfLines);

                sw.WriteLine("</pre>"); //line-numbers
                sw.WriteLine("</td>");

                sw.WriteLine("<td valign='top'>");
                sw.WriteLine("<pre class='source-code'>");
                

                foreach (var token in documentModel.Tokens)
                {
                    processToken(sw, token);
                }

                sw.WriteLine("</pre>");
                sw.WriteLine("</td>");
                sw.WriteLine("</tr>");
                sw.WriteLine("</tbody>");
                sw.WriteLine("</table>");

            }

            base.VisitDocument(documentModel);
        }

        private void createLineNumberHtml(StreamWriter sw, int numberOfLines)
        {
            //Note that we start at line 1, as most editors dont start at line 0.
            for(int i = 1; i < numberOfLines; i++)
            {
                sw.WriteLine("<a href='#" + i + "' name='" + i + "'>" + i + "</a>");
            }
        }

        private void processToken(StreamWriter sw, Token token)
        {
            processTriviaCollection(sw, token.LeadingTrivia);

            sw.Write("<span class='");
            sw.Write(HttpUtility.HtmlEncode(token.Type));
            sw.Write("'>");
            if(token.Link != null)
            {
                sw.Write("<a href='");

                var symbolLink = token.Link as SymbolLink;
                if (symbolLink != null)
                    processSymbolLink(sw, token);

                var urlLink = token.Link as UrlLink;
                if (urlLink != null)
                    processUrlLink(sw, token);

                sw.Write("'>");
                sw.Write(HttpUtility.HtmlEncode(token.Value));
                sw.Write("</a>");

            }
            else
            {
                sw.Write(HttpUtility.HtmlEncode(token.Value));
            }

            sw.Write("</span>");
            processTriviaCollection(sw, token.TrailingTrivia);
        }

        private void processUrlLink(StreamWriter sw, Token token)
        {
            var urlLink = token.Link as UrlLink;
            var url = urlLink.Url;
            sw.Write(url);
        }

        private void processSymbolLink(StreamWriter sw, Token token)
        {
            var symbolLink = token.Link as SymbolLink;
            var name = symbolLink.ReferencedSymbolName;
            Token referencedToken;
            if(_tokenLookup.TryGetValue(name, out referencedToken))
            {
                var relPath = Utilities.MakeRelativePath(token.Document.RelativePath, referencedToken.Document.RelativePath);
                var path = relPath + "#" + referencedToken.LineNumber.ToString();
                sw.Write(path);
            }
            else
            {
                //If we can't find it, just make the link point nowhere.
                sw.Write('#');
            }
        }


        private void processTriviaCollection(StreamWriter sw, ICollection<Trivia> triviaCollection)
        {
            foreach(var trivia in triviaCollection)
            {
                processTrivia(sw, trivia);
            }
        }

        private void processTrivia(StreamWriter sw, Trivia trivia)
        {
            if(trivia.Type.Contains("Comment"))
            {
                sw.Write("<span class='Comment'>");
                sw.Write(HttpUtility.HtmlEncode(trivia.Value));
                sw.Write("</span>");
            }
            else
            {
                sw.Write(HttpUtility.HtmlEncode(trivia.Value));
            }
        }
    }
}