HTMLRewrite.cpp revision a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2
123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
2a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)//
3a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)//                     The LLVM Compiler Infrastructure
4a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)//
5effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// This file is distributed under the University of Illinois Open Source
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// License. See LICENSE.TXT for details.
7effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch//
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)//===----------------------------------------------------------------------===//
9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch//
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)//  This file defines the HTMLRewriter clas, which is used to translate the
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//  text of a source file into prettified HTML.
12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)//
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//===----------------------------------------------------------------------===//
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "clang/Rewrite/Rewriter.h"
16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "clang/Rewrite/HTMLRewrite.h"
17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "clang/Lex/Preprocessor.h"
18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "clang/Basic/SourceManager.h"
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "llvm/ADT/SmallString.h"
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "llvm/Support/MemoryBuffer.h"
21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include <sstream>
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using namespace clang;
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void html::EscapeText(Rewriter& R, unsigned FileID,
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      bool EscapeSpaces, bool ReplaceTabs) {
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const char* C = Buf->getBufferStart();
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const char* FileEnd = Buf->getBufferEnd();
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  assert (C <= FileEnd);
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  RewriteBuffer &RB = R.getEditBuffer(FileID);
34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    switch (*C) {
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    default: break;
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case ' ':
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (EscapeSpaces)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RB.ReplaceText(FilePos, 1, "&nbsp;", 6);
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      break;
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case '\t':
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (!ReplaceTabs)
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        break;
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (EscapeSpaces)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RB.ReplaceText(FilePos, 1, "&nbsp;&nbsp;&nbsp;&nbsp;", 6*4);
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      else
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RB.ReplaceText(FilePos, 1, "    ", 4);
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case '<':
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      RB.ReplaceText(FilePos, 1, "&lt;", 4);
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case '>':
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      RB.ReplaceText(FilePos, 1, "&gt;", 4);
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      break;
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case '&':
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      RB.ReplaceText(FilePos, 1, "&amp;", 5);
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      break;
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)std::string html::EscapeText(const std::string& s, bool EscapeSpaces,
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                             bool ReplaceTabs) {
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  unsigned len = s.size();
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::ostringstream os;
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (unsigned i = 0 ; i < len; ++i) {
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    char c = s[i];
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    switch (c) {
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    default:
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      os << c; break;
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case ' ':
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (EscapeSpaces) os << "&nbsp;";
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      else os << ' ';
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      case '\t':
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (ReplaceTabs)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          for (unsigned i = 0; i < 4; ++i)
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            os << "&nbsp;";
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        else
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          os << c;
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      case '<': os << "&lt;"; break;
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      case '>': os << "&gt;"; break;
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      case '&': os << "&amp;"; break;
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return os.str();
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static void AddLineNumber(RewriteBuffer &RB, unsigned LineNo,
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                          unsigned B, unsigned E) {
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  llvm::SmallString<100> Str;
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Str += "<tr><td class=\"num\" id=\"LN";
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Str.append_uint(LineNo);
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Str += "\">";
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Str.append_uint(LineNo);
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Str += "</td><td class=\"line\">";
113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (B == E) { // Handle empty lines.
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    Str += " </td></tr>";
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    RB.InsertTextBefore(B, &Str[0], Str.size());
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  } else {
118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    RB.InsertTextBefore(B, &Str[0], Str.size());
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    RB.InsertTextBefore(E, "</td></tr>", strlen("</td></tr>"));
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void html::AddLineNumbers(Rewriter& R, unsigned FileID) {
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const char* FileBeg = Buf->getBufferStart();
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const char* FileEnd = Buf->getBufferEnd();
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const char* C = FileBeg;
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  RewriteBuffer &RB = R.getEditBuffer(FileID);
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  assert (C <= FileEnd);
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  unsigned LineNo = 0;
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  unsigned FilePos = 0;
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  while (C != FileEnd) {
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ++LineNo;
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    unsigned LineStartPos = FilePos;
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    unsigned LineEndPos = FileEnd - FileBeg;
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    assert (FilePos <= LineEndPos);
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    assert (C < FileEnd);
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Scan until the newline (or end-of-file).
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    while (C != FileEnd) {
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      char c = *C;
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ++C;
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (c == '\n') {
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        LineEndPos = FilePos++;
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ++FilePos;
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    AddLineNumber(RB, LineNo, LineStartPos, LineEndPos);
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Add one big table tag that surrounds all of the code.
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  RB.InsertTextBefore(0, "<table class=\"code\">\n",
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      strlen("<table class=\"code\">\n"));
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  RB.InsertTextAfter(FileEnd - FileBeg, "</table>", strlen("</table>"));
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, unsigned FileID) {
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const char* FileStart = Buf->getBufferStart();
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const char* FileEnd = Buf->getBufferEnd();
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  SourceLocation StartLoc = SourceLocation::getFileLoc(FileID, 0);
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  SourceLocation EndLoc = SourceLocation::getFileLoc(FileID, FileEnd-FileStart);
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Generate header
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  R.InsertCStrBefore(StartLoc,
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      "<html>\n<head>\n"
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      "<style type=\"text/css\">\n"
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " body { color:#000000; background-color:#ffffff }\n"
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " body { font-family:Helvetica, sans-serif; font-size:10pt }\n"
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " h1 { font-size:14pt }\n"
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .code { border-spacing:0px; width:100%; }\n"
186a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .code { font-family: \"Andale Mono\", monospace; font-size:10pt }\n"
187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .code { line-height: 1.2em }\n"
188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .comment { color: #A0A0A0 }\n"
189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .keyword { color: #FF00FF }\n"
190a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .directive { color: #FFFF00 }\n"
191a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .macro { color: #FF0000; background-color:#FFC0C0 }\n"
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .num { width:2.5em; padding-right:2ex; background-color:#eeeeee }\n"
193a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .num { text-align:right; font-size: smaller }\n"
194a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .num { color:#444444 }\n"
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .line { padding-left: 1ex; border-left: 3px solid #ccc }\n"
196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .line { white-space: pre }\n"
197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .msg { background-color:#fff8b4; color:#000000 }\n"
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { -webkit-box-shadow:1px 1px 7px #000 }\n"
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { -webkit-border-radius:5px }\n"
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { font-family:Helvetica, sans-serif; font-size: smaller }\n"
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { font-weight: bold }\n"
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { float:left }\n"
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { padding:0.5em 1ex 0.5em 1ex }\n"
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .msg { margin-top:10px; margin-bottom:10px }\n"
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .mrange { background-color:#dfddf3 }\n"
206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      " .mrange { border-bottom:1px solid #6F9DBE }\n"
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " .PathIndex { font-weight: bold }\n"
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " table.simpletable {\n"
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "   padding: 5px;\n"
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "   font-size:12pt;\n"
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "   margin:20px;\n"
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "   border-collapse: collapse; border-spacing: 0px;\n"
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " }\n"
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      " td.rowname {\n"
2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "   text-align:right; font-weight:bold; color:#444444;\n"
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "   padding-right:2ex; }\n"
217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      "</style>\n</head>\n<body>");
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Generate footer
220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  R.InsertCStrAfter(EndLoc, "</body></html>\n");
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
223a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
225a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)/// information about keywords, macro expansions etc.  This uses the macro
226116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/// table state from the end of the file, so it won't be perfectly perfect,
227116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/// but it will be reasonably close.
228116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid html::SyntaxHighlight(Rewriter &R, unsigned FileID, Preprocessor &PP) {
229116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  RewriteBuffer &RB = R.getEditBuffer(FileID);
230116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
231116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  const SourceManager &SourceMgr = PP.getSourceManager();
232116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  std::pair<const char*, const char*> File = SourceMgr.getBufferData(FileID);
233116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  const char *BufferStart = File.first;
234116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
235116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  Lexer L(SourceLocation::getFileLoc(FileID, 0), PP.getLangOptions(),
236116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          File.first, File.second);
237116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
238116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Inform the preprocessor that we want to retain comments as tokens, so we
239116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // can highlight them.
240116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  //PP.SetCommentRetentionState(true, false);
241116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
242116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
243116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // macros.
244116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  Token Tok;
245116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  L.LexRawToken(Tok);
246116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
247116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  while (Tok.isNot(tok::eof)) {
248116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Since we are lexing unexpanded tokens, all tokens are from the main
249116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // FileID.
250116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    unsigned TokOffs = SourceMgr.getFullFilePos(Tok.getLocation());
251a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    unsigned TokLen = Tok.getLength();
252a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    switch (Tok.getKind()) {
253116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    default: break;
254116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    case tok::identifier: {
255116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      // Fill in Result.IdentifierInfo, looking up the identifier in the
256116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      // identifier table.
257116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      IdentifierInfo *II = PP.LookUpIdentifierInfo(Tok, BufferStart+TokOffs);
258a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
259a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      // If this is a pp-identifier, for a keyword, highlight it as such.
260a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (II->getTokenID() != tok::identifier) {
261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        RB.InsertTextAfter(TokOffs, "<span class='keyword'>",
262a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                           strlen("<span class='keyword'>"));
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RB.InsertTextBefore(TokOffs+TokLen, "</span>", strlen("</span>"));
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case tok::comment:
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      RB.InsertTextAfter(TokOffs, "<span class='comment'>",
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         strlen("<span class='comment'>"));
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      RB.InsertTextBefore(TokOffs+TokLen, "</span>", strlen("</span>"));
2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case tok::hash:
2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // FIXME: This isn't working because we're not in raw mode in the lexer.
2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Just cons up our own lexer here?
2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // If this is a preprocessor directive, all tokens to end of line are too.
2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (Tok.isAtStartOfLine()) {
2785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RB.InsertTextAfter(TokOffs, "<span class='directive'>",
2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                           strlen("<span class='directive'>"));
2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        // Find end of line.  This is a hack.
2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        const char *LineEnd = SourceMgr.getCharacterData(Tok.getLocation());
2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        unsigned TokEnd = TokOffs+strcspn(LineEnd, "\n\r");
2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RB.InsertTextBefore(TokEnd, "</span>", strlen("</span>"));
2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
2865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    L.LexRawToken(Tok);
2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/// HighlightMacros - This uses the macro table state from the end of the
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/// file, to reexpand macros and insert (into the HTML) information about the
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/// macro expansions.  This won't be perfectly perfect, but it will be
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/// reasonably close.
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void html::HighlightMacros(Rewriter &R, unsigned FileID, Preprocessor &PP) {
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  RewriteBuffer &RB = R.getEditBuffer(FileID);
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Inform the preprocessor that we don't want comments.
300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  PP.SetCommentRetentionState(false, false);
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Start parsing the specified input file.
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PP.EnterMainSourceFile();
304116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Lex all the tokens.
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const SourceManager &SourceMgr = PP.getSourceManager();
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Token Tok;
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PP.Lex(Tok);
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  while (Tok.isNot(tok::eof)) {
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Ignore non-macro tokens.
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (!Tok.getLocation().isMacroID()) {
312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      PP.Lex(Tok);
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Ignore tokens whose logical location was not the main file.
3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SourceLocation LLoc = SourceMgr.getLogicalLoc(Tok.getLocation());
3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::pair<unsigned, unsigned> LLocInfo =
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      SourceMgr.getDecomposedFileLoc(LLoc);
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (LLocInfo.first != FileID) {
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      PP.Lex(Tok);
3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Okay, we have the first token of a macro expansion: highlight the
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // instantiation.
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Get the size of current macro call itself.
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // FIXME: This should highlight the args of a function-like
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // macro, using a heuristic.
332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    unsigned TokLen = Lexer::MeasureTokenLength(LLoc, SourceMgr);
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    unsigned TokOffs = LLocInfo.second;
335116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    RB.InsertTextAfter(TokOffs, "<span class='macro'>",
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                       strlen("<span class='macro'>"));
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RB.InsertTextBefore(TokOffs+TokLen, "</span>", strlen("</span>"));
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Okay, eat this token, getting the next one.
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    PP.Lex(Tok);
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Skip all the rest of the tokens that are part of this macro
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // instantiation.  It would be really nice to pop up a window with all the
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // spelling of the tokens or something.
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    while (!Tok.is(tok::eof) &&
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           SourceMgr.getLogicalLoc(Tok.getLocation()) == LLoc)
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      PP.Lex(Tok);
348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)