HTMLRewrite.cpp revision 4b0f81323b518429203051bbcd4864bbf4b000a9
1//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  This file defines the HTMLRewriter clas, which is used to translate the
11//  text of a source file into prettified HTML.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Rewrite/Rewriter.h"
16#include "clang/Rewrite/HTMLRewrite.h"
17#include "clang/Basic/SourceManager.h"
18#include "llvm/Support/MemoryBuffer.h"
19#include <sstream>
20
21using namespace clang;
22
23void html::EscapeText(Rewriter& R, unsigned FileID,
24                      bool EscapeSpaces, bool ReplaceTabs) {
25
26  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
27  const char* C = Buf->getBufferStart();
28  const char* FileEnd = Buf->getBufferEnd();
29
30  assert (C <= FileEnd);
31
32  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {
33
34    switch (*C) {
35      default: break;
36
37      case ' ':
38        if (EscapeSpaces) {
39          SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
40          R.ReplaceText(Loc, 1, "&nbsp;", 6);
41        }
42        break;
43
44      case '\t': {
45        if (!ReplaceTabs)
46          break;
47
48        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
49
50        if (EscapeSpaces)
51          R.ReplaceText(Loc, 1, "&nbsp;&nbsp;&nbsp;&nbsp;", 6*4);
52        else
53          R.ReplaceText(Loc, 1, "    ", 4);
54
55        break;
56      }
57
58      case '<': {
59        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
60        R.ReplaceText(Loc, 1, "&lt;", 4);
61        break;
62      }
63
64      case '>': {
65        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
66        R.ReplaceText(Loc, 1, "&gt;", 4);
67        break;
68      }
69
70      case '&': {
71        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
72        R.ReplaceText(Loc, 1, "&amp;", 5);
73        break;
74      }
75    }
76  }
77}
78
79std::string html::EscapeText(const std::string& s, bool EscapeSpaces,
80                             bool ReplaceTabs) {
81
82  unsigned len = s.size();
83  std::ostringstream os;
84
85  for (unsigned i = 0 ; i < len; ++i) {
86
87    char c = s[i];
88
89    switch (c) {
90      default:
91        os << c; break;
92
93      case ' ':
94        if (EscapeSpaces) os << "&nbsp;";
95        else os << ' ';
96        break;
97
98        case '\t':
99          if (ReplaceTabs)
100            for (unsigned i = 0; i < 4; ++i) os << "&nbsp;";
101          else os << c;
102
103          break;
104
105        case '<': os << "&lt;"; break;
106        case '>': os << "&gt;"; break;
107        case '&': os << "&amp;"; break;
108    }
109  }
110
111  return os.str();
112}
113
114static void AddLineNumber(Rewriter& R, unsigned LineNo,
115                          SourceLocation B, SourceLocation E) {
116
117  std::ostringstream os;
118  os << "<tr><td class=\"num\" id=\"LN" << LineNo << "\">"
119     << LineNo << "</td><td class=\"line\">";
120
121  if (B == E) { // Handle empty lines.
122    os << " </td></tr>";
123    R.InsertStrBefore(B, os.str());
124  }
125  else {
126    R.InsertStrBefore(B, os.str());
127    R.InsertCStrBefore(E, "</td></tr>");
128  }
129}
130
131void html::AddLineNumbers(Rewriter& R, unsigned FileID) {
132
133  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
134  const char* FileBeg = Buf->getBufferStart();
135  const char* FileEnd = Buf->getBufferEnd();
136  const char* C = FileBeg;
137
138  assert (C <= FileEnd);
139
140  unsigned LineNo = 0;
141  unsigned FilePos = 0;
142
143  while (C != FileEnd) {
144
145    ++LineNo;
146    unsigned LineStartPos = FilePos;
147    unsigned LineEndPos = FileEnd - FileBeg;
148
149    assert (FilePos <= LineEndPos);
150    assert (C < FileEnd);
151
152    // Scan until the newline (or end-of-file).
153
154    while (C != FileEnd) {
155      char c = *C;
156      ++C;
157
158      if (c == '\n') {
159        LineEndPos = FilePos++;
160        break;
161      }
162
163      ++FilePos;
164    }
165
166    AddLineNumber(R, LineNo,
167                  SourceLocation::getFileLoc(FileID, LineStartPos),
168                  SourceLocation::getFileLoc(FileID, LineEndPos));
169  }
170
171  // Add one big div tag that surrounds all of the code.
172
173  R.InsertCStrBefore(SourceLocation::getFileLoc(FileID, 0),
174                     "<table class=\"code\">\n");
175
176  R.InsertCStrAfter(SourceLocation::getFileLoc(FileID, FileEnd - FileBeg),
177                    "</table>");
178}
179
180void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, unsigned FileID) {
181
182  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
183  const char* FileStart = Buf->getBufferStart();
184  const char* FileEnd = Buf->getBufferEnd();
185
186  SourceLocation StartLoc = SourceLocation::getFileLoc(FileID, 0);
187  SourceLocation EndLoc = SourceLocation::getFileLoc(FileID, FileEnd-FileStart);
188
189  // Generate header
190
191  R.InsertCStrBefore(StartLoc,
192      "<html>\n<head>\n"
193      "<style type=\"text/css\">\n"
194      " body { color:#000000; background-color:#ffffff }\n"
195      " body { font-family:Helvetica, sans-serif; font-size:10pt }\n"
196      " h1 { font-size:14pt }\n"
197      " .code { border-spacing:0px; width:100%; }\n"
198      " .code { font-family: \"Andale Mono\", monospace; font-size:10pt }\n"
199      " .code { line-height: 1.2em }\n"
200      " .num { width:2.5em; padding-right:2ex; background-color:#eeeeee }\n"
201      " .num { text-align:right; font-size: smaller }\n"
202      " .num { color:#444444 }\n"
203      " .line { padding-left: 1ex; border-left: 3px solid #ccc }\n"
204      " .line { white-space: pre }\n"
205      " .msg { background-color:#fff8b4; color:#000000 }\n"
206      " .msg { -webkit-box-shadow:1px 1px 7px #000 }\n"
207      " .msg { -webkit-border-radius:5px }\n"
208      " .msg { font-family:Helvetica, sans-serif; font-size: smaller }\n"
209      " .msg { font-weight: bold }\n"
210      " .msg { float:left }\n"
211      " .msg { padding:0.5em 1ex 0.5em 1ex }\n"
212      " .msg { margin-top:10px; margin-bottom:10px }\n"
213      " .mrange { background-color:#dfddf3 }\n"
214      " .mrange { border-bottom:1px solid #6F9DBE }\n"
215      " .PathIndex { font-weight: bold }\n"
216      " table.simpletable {\n"
217      "   padding: 5px;\n"
218      "   font-size:12pt;\n"
219      "   margin:20px;\n"
220      "   border-collapse: collapse; border-spacing: 0px;\n"
221      " }\n"
222      " td.rowname {\n"
223      "   text-align:right; font-weight:bold; color:#444444;\n"
224      "   padding-right:2ex; }\n"
225      "</style>\n</head>\n<body>");
226
227  // Generate footer
228
229  R.InsertCStrAfter(EndLoc, "</body></html>\n");
230}
231