HTMLRewrite.cpp revision b23b711ad3dfb96dc9c457bd55c6e959bd1e0b8a
16a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
26a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
36a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//                     The LLVM Compiler Infrastructure
46a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
56a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek// This file is distributed under the University of Illinois Open Source
66a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek// License. See LICENSE.TXT for details.
76a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
86a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//===----------------------------------------------------------------------===//
96a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
106a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//  This file defines the HTMLRewriter clas, which is used to translate the
116a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//  text of a source file into prettified HTML.
126a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//
136a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek//===----------------------------------------------------------------------===//
146a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
15339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek#include "clang/Lex/Preprocessor.h"
166a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Rewrite/Rewriter.h"
176a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Rewrite/HTMLRewrite.h"
18867924dbeca06870573fd58d620032da6994b223Chris Lattner#include "clang/Lex/TokenConcatenation.h"
193245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner#include "clang/Lex/Preprocessor.h"
206a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Basic/SourceManager.h"
2157df3b950061c73d13d3116f747e79d7955a216aChris Lattner#include "llvm/ADT/SmallString.h"
22339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek#include "llvm/ADT/OwningPtr.h"
23c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara#include "llvm/Support/ErrorHandling.h"
246a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "llvm/Support/MemoryBuffer.h"
25a95d3750441ac8ad03e36af8e6e74039c9a3109dTed Kremenek#include "llvm/Support/raw_ostream.h"
266a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenekusing namespace clang;
276a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
289402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
295ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// HighlightRange - Highlight a range in the source code with the specified
305ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// start/end tags.  B/E must be in the same file.  This ensures that
315ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// start/end tags are placed at the start/end of each line if the range is
325ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// multiline.
335ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattnervoid html::HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E,
345ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *StartTag, const char *EndTag) {
355ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  SourceManager &SM = R.getSourceMgr();
36402785357ab053dd53f4fdd858b9630a5e0f8badChandler Carruth  B = SM.getExpansionLoc(B);
37402785357ab053dd53f4fdd858b9630a5e0f8badChandler Carruth  E = SM.getExpansionLoc(E);
38a11d61793341fea195c29a0dab3fbd74f2b39a8cChris Lattner  FileID FID = SM.getFileID(B);
39a11d61793341fea195c29a0dab3fbd74f2b39a8cChris Lattner  assert(SM.getFileID(E) == FID && "B/E not in the same file!");
405ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
4152c29081281955d3db9e11d10573b2d38f709099Chris Lattner  unsigned BOffset = SM.getFileOffset(B);
4252c29081281955d3db9e11d10573b2d38f709099Chris Lattner  unsigned EOffset = SM.getFileOffset(E);
431eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
445ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  // Include the whole end token in the range.
452c78b873f4f3823ae859c15674cb3d76c8554113Chris Lattner  EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr(), R.getLangOpts());
461eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
47f715ca12bfc9fddfde75f98a197424434428b821Douglas Gregor  bool Invalid = false;
48f6ac97b101c8840efa92bf29166077ce4049e293Benjamin Kramer  const char *BufferStart = SM.getBufferData(FID, &Invalid).data();
49f715ca12bfc9fddfde75f98a197424434428b821Douglas Gregor  if (Invalid)
50aea67dbd653a2dd6dd5cc2159279e81e855b2482Douglas Gregor    return;
51aea67dbd653a2dd6dd5cc2159279e81e855b2482Douglas Gregor
522b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  HighlightRange(R.getEditBuffer(FID), BOffset, EOffset,
53aea67dbd653a2dd6dd5cc2159279e81e855b2482Douglas Gregor                 BufferStart, StartTag, EndTag);
545ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner}
555ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
565ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// HighlightRange - This is the same as the above method, but takes
575ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// decomposed file locations.
585ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattnervoid html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
595ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *BufferStart,
605ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *StartTag, const char *EndTag) {
619402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // Insert the tag at the absolute start/end of the range.
62d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar  RB.InsertTextAfter(B, StartTag);
63d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar  RB.InsertTextBefore(E, EndTag);
641eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
659402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // Scan the range to see if there is a \r or \n.  If so, and if the line is
669402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // not blank, insert tags on that line as well.
679402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  bool HadOpenTag = true;
681eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
699402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  unsigned LastNonWhiteSpace = B;
709402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  for (unsigned i = B; i != E; ++i) {
719402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    switch (BufferStart[i]) {
729402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\r':
739402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\n':
749402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Okay, we found a newline in the range.  If we have an open tag, we need
759402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // to insert a close tag at the first non-whitespace before the newline.
769402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      if (HadOpenTag)
77d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar        RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag);
781eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
799402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Instead of inserting an open tag immediately after the newline, we
809402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // wait until we see a non-whitespace character.  This prevents us from
819402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // inserting tags around blank lines, and also allows the open tag to
829402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // be put *after* whitespace on a non-blank line.
839402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      HadOpenTag = false;
849402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
859402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\0':
869402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case ' ':
879402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\t':
889402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\f':
899402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\v':
909402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Ignore whitespace.
919402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
921eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
939402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    default:
949402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // If there is no tag open, do it now.
959402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      if (!HadOpenTag) {
96d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar        RB.InsertTextAfter(i, StartTag);
979402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        HadOpenTag = true;
989402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      }
991eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1009402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Remember this character.
1019402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      LastNonWhiteSpace = i;
1029402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
1039402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    }
1049402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  }
1055ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner}
1065ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
1072b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattnervoid html::EscapeText(Rewriter &R, FileID FID,
108fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenek                      bool EscapeSpaces, bool ReplaceTabs) {
1091eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1102b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID);
1116a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const char* C = Buf->getBufferStart();
1126a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
1131eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1146a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  assert (C <= FileEnd);
1151eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1162b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FID);
1175c176f7a9ba9f7084b903393845be24f85e091daChris Lattner
1185c176f7a9ba9f7084b903393845be24f85e091daChris Lattner  unsigned ColNo = 0;
1196a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {
1206a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek    switch (*C) {
1215c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    default: ++ColNo; break;
1225c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    case '\n':
1235c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    case '\r':
1245c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ColNo = 0;
1255c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      break;
1261eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
127735271479ac57c27f744806859efd5b001dea248Chris Lattner    case ' ':
128735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (EscapeSpaces)
129d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar        RB.ReplaceText(FilePos, 1, "&nbsp;");
1305c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
131735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
132f3d8d19caf6f1a21785eee8d62c45ef5a0a0e72eChris Lattner    case '\f':
133d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar      RB.ReplaceText(FilePos, 1, "<hr>");
134f3d8d19caf6f1a21785eee8d62c45ef5a0a0e72eChris Lattner      ColNo = 0;
135f3d8d19caf6f1a21785eee8d62c45ef5a0a0e72eChris Lattner      break;
1361eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1375c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    case '\t': {
138735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (!ReplaceTabs)
13949cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek        break;
1405c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      unsigned NumSpaces = 8-(ColNo&7);
141735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (EscapeSpaces)
142d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar        RB.ReplaceText(FilePos, 1,
1435f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner                       StringRef("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
144d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar                                       "&nbsp;&nbsp;&nbsp;", 6*NumSpaces));
145735271479ac57c27f744806859efd5b001dea248Chris Lattner      else
1465f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner        RB.ReplaceText(FilePos, 1, StringRef("        ", NumSpaces));
1475c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ColNo += NumSpaces;
148735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1495c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    }
150735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '<':
151d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar      RB.ReplaceText(FilePos, 1, "&lt;");
1525c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
153735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1541eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
155735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '>':
156d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar      RB.ReplaceText(FilePos, 1, "&gt;");
1575c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
158735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1591eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
160735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '&':
161d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar      RB.ReplaceText(FilePos, 1, "&amp;");
1625c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
163735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1646a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek    }
1656a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  }
1666a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek}
1676a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
168fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenekstd::string html::EscapeText(const std::string& s, bool EscapeSpaces,
169fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenek                             bool ReplaceTabs) {
1701eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
171053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  unsigned len = s.size();
172a95d3750441ac8ad03e36af8e6e74039c9a3109dTed Kremenek  std::string Str;
173a95d3750441ac8ad03e36af8e6e74039c9a3109dTed Kremenek  llvm::raw_string_ostream os(Str);
1741eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
175053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  for (unsigned i = 0 ; i < len; ++i) {
1761eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
177053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    char c = s[i];
178053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    switch (c) {
1798570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    default:
1808570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      os << c; break;
1811eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1828570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    case ' ':
1838570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      if (EscapeSpaces) os << "&nbsp;";
1848570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      else os << ' ';
1858570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      break;
1861eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
187c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu    case '\t':
188c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu      if (ReplaceTabs) {
189c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu        if (EscapeSpaces)
190c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu          for (unsigned i = 0; i < 4; ++i)
191c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu            os << "&nbsp;";
192c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu        else
193c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu          for (unsigned i = 0; i < 4; ++i)
194c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu            os << " ";
195c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu      }
1961eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      else
197c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu        os << c;
1981eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
199c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu      break;
2001eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
201c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu    case '<': os << "&lt;"; break;
202c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu    case '>': os << "&gt;"; break;
203c01b46e2f115bed83f2f6f4a55809bf4003ee015Zhongxing Xu    case '&': os << "&amp;"; break;
204053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    }
205053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  }
2061eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
207053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  return os.str();
208053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek}
209053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
2108570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattnerstatic void AddLineNumber(RewriteBuffer &RB, unsigned LineNo,
2118570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner                          unsigned B, unsigned E) {
212f7ccbad5d9949e7ddd1cbef43d482553b811e026Dylan Noblesmith  SmallString<256> Str;
2137e37c818f9f77608c602ffb32c1135e3cd0132a8Daniel Dunbar  llvm::raw_svector_ostream OS(Str);
2147e37c818f9f77608c602ffb32c1135e3cd0132a8Daniel Dunbar
2157e37c818f9f77608c602ffb32c1135e3cd0132a8Daniel Dunbar  OS << "<tr><td class=\"num\" id=\"LN"
2167e37c818f9f77608c602ffb32c1135e3cd0132a8Daniel Dunbar     << LineNo << "\">"
2177e37c818f9f77608c602ffb32c1135e3cd0132a8Daniel Dunbar     << LineNo << "</td><td class=\"line\">";
2181eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
21949cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek  if (B == E) { // Handle empty lines.
2207e37c818f9f77608c602ffb32c1135e3cd0132a8Daniel Dunbar    OS << " </td></tr>";
221d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar    RB.InsertTextBefore(B, OS.str());
22257df3b950061c73d13d3116f747e79d7955a216aChris Lattner  } else {
223d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar    RB.InsertTextBefore(B, OS.str());
224d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar    RB.InsertTextBefore(E, "</td></tr>");
22549cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek  }
226b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek}
227b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
2282b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattnervoid html::AddLineNumbers(Rewriter& R, FileID FID) {
229b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
2302b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID);
231b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* FileBeg = Buf->getBufferStart();
232b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
233b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* C = FileBeg;
2342b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FID);
2351eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
236b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  assert (C <= FileEnd);
2371eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
238b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  unsigned LineNo = 0;
239b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  unsigned FilePos = 0;
2401eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
2411eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  while (C != FileEnd) {
2421eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
243b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    ++LineNo;
244b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    unsigned LineStartPos = FilePos;
245b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    unsigned LineEndPos = FileEnd - FileBeg;
2461eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
247b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    assert (FilePos <= LineEndPos);
248b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    assert (C < FileEnd);
2491eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
250b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    // Scan until the newline (or end-of-file).
2511eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
25249cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek    while (C != FileEnd) {
25349cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      char c = *C;
25449cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      ++C;
2551eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
25649cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      if (c == '\n') {
25749cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek        LineEndPos = FilePos++;
258b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek        break;
259b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek      }
2601eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
26149cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      ++FilePos;
26249cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek    }
2631eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
2648570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    AddLineNumber(RB, LineNo, LineStartPos, LineEndPos);
265d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek  }
2661eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
2678570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  // Add one big table tag that surrounds all of the code.
268d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar  RB.InsertTextBefore(0, "<table class=\"code\">\n");
269d7407dc92c7d19cafce429e7e1cf9819d3fc0b92Daniel Dunbar  RB.InsertTextAfter(FileEnd - FileBeg, "</table>");
270b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek}
271ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
2721eb4433ac451dc16f4133a88af2d002ac26c58efMike Stumpvoid html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID,
273f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek                                             const char *title) {
274ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
2752b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID);
276ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const char* FileStart = Buf->getBufferStart();
277ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
278ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
2792b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  SourceLocation StartLoc = R.getSourceMgr().getLocForStartOfFile(FID);
280a64ccefdf0ea4e03ec88805d71b0af74950c7472Argyrios Kyrtzidis  SourceLocation EndLoc = StartLoc.getLocWithOffset(FileEnd-FileStart);
281ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
282a95d3750441ac8ad03e36af8e6e74039c9a3109dTed Kremenek  std::string s;
283a95d3750441ac8ad03e36af8e6e74039c9a3109dTed Kremenek  llvm::raw_string_ostream os(s);
284f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek  os << "<!doctype html>\n" // Use HTML 5 doctype
285f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek        "<html>\n<head>\n";
2861eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
287f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek  if (title)
288f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek    os << "<title>" << html::EscapeText(title) << "</title>\n";
2891eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
290f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek  os << "<style type=\"text/css\">\n"
29170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " body { color:#000000; background-color:#ffffff }\n"
29270bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " body { font-family:Helvetica, sans-serif; font-size:10pt }\n"
2934b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " h1 { font-size:14pt }\n"
294f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .code { border-collapse:collapse; width:100%; }\n"
2953eaaa99c7ee85300e2c55dcc2103ebeae19f4d11Ted Kremenek      " .code { font-family: \"Monospace\", monospace; font-size:10pt }\n"
29670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .code { line-height: 1.2em }\n"
297f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .comment { color: green; font-style: oblique }\n"
298f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .keyword { color: blue }\n"
299cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek      " .string_literal { color: red }\n"
300f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .directive { color: darkmagenta }\n"
3016f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      // Macro expansions.
30207339a63b46e38c954fcccbef721c609d0c2040eTed Kremenek      " .expansion { display: none; }\n"
30307339a63b46e38c954fcccbef721c609d0c2040eTed Kremenek      " .macro:hover .expansion { display: block; border: 2px solid #FF0000; "
304dc5be47542e6d4a28d20abf9c0f0a0edd72939b6Chris Lattner          "padding: 2px; background-color:#FFF0F0; font-weight: normal; "
3056f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner          "  -webkit-border-radius:5px;  -webkit-box-shadow:1px 1px 7px #000; "
3068aa06aca8b2d3771a5405d789b2e704149045dd4Chris Lattner          "position: absolute; top: -1em; left:10em; z-index: 1 } \n"
307f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .macro { color: darkmagenta; background-color:LemonChiffon;"
3086f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner             // Macros are position: relative to provide base for expansions.
3096f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner             " position: relative }\n"
31070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { width:2.5em; padding-right:2ex; background-color:#eeeeee }\n"
3112223622d113d7cba04c2dfdbe032e2ba6ba10bc4Ted Kremenek      " .num { text-align:right; font-size:8pt }\n"
31270bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { color:#444444 }\n"
31370bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .line { padding-left: 1ex; border-left: 3px solid #ccc }\n"
31470bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .line { white-space: pre }\n"
31570bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { -webkit-box-shadow:1px 1px 7px #000 }\n"
31670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { -webkit-border-radius:5px }\n"
3172223622d113d7cba04c2dfdbe032e2ba6ba10bc4Ted Kremenek      " .msg { font-family:Helvetica, sans-serif; font-size:8pt }\n"
31870bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { float:left }\n"
3193c59823096fe28a69a81f899c7f9f0e6eb8071a0Ted Kremenek      " .msg { padding:0.25em 1ex 0.25em 1ex }\n"
32070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { margin-top:10px; margin-bottom:10px }\n"
3212f10398814e8d58cce029a7e73af21bb274dcd42Ted Kremenek      " .msg { font-weight:bold }\n"
32280bae763da8bf3f3c73379a1e5d10f5bce266bcaTed Kremenek      " .msg { max-width:60em; word-wrap: break-word; white-space: pre-wrap }\n"
32380bae763da8bf3f3c73379a1e5d10f5bce266bcaTed Kremenek      " .msgT { padding:0x; spacing:0x }\n"
3242f10398814e8d58cce029a7e73af21bb274dcd42Ted Kremenek      " .msgEvent { background-color:#fff8b4; color:#000000 }\n"
32580bae763da8bf3f3c73379a1e5d10f5bce266bcaTed Kremenek      " .msgControl { background-color:#bbbbbb; color:#000000 }\n"
32670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .mrange { background-color:#dfddf3 }\n"
32770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .mrange { border-bottom:1px solid #6F9DBE }\n"
328b23b711ad3dfb96dc9c457bd55c6e959bd1e0b8aJordan Rose      " .PathIndex { font-weight: bold; padding:0px 5px; "
3293c59823096fe28a69a81f899c7f9f0e6eb8071a0Ted Kremenek        "margin-right:5px; }\n"
33000f01e440573b2bdf6549991e0ca48cc6747e3a2Ted Kremenek      " .PathIndex { -webkit-border-radius:8px }\n"
33100f01e440573b2bdf6549991e0ca48cc6747e3a2Ted Kremenek      " .PathIndexEvent { background-color:#bfba87 }\n"
33200f01e440573b2bdf6549991e0ca48cc6747e3a2Ted Kremenek      " .PathIndexControl { background-color:#8c8c8c }\n"
333b23b711ad3dfb96dc9c457bd55c6e959bd1e0b8aJordan Rose      " .PathNav a { text-decoration:none; font-size: larger }\n"
3344b2d3f7bcc4df31157df443af1b80bcaa9b58bbaDouglas Gregor      " .CodeInsertionHint { font-weight: bold; background-color: #10dd10 }\n"
3354b2d3f7bcc4df31157df443af1b80bcaa9b58bbaDouglas Gregor      " .CodeRemovalHint { background-color:#de1010 }\n"
3364b2d3f7bcc4df31157df443af1b80bcaa9b58bbaDouglas Gregor      " .CodeRemovalHint { border-bottom:1px solid #6F9DBE }\n"
3374b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " table.simpletable {\n"
3384b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   padding: 5px;\n"
3394b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   font-size:12pt;\n"
3404b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   margin:20px;\n"
3414b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   border-collapse: collapse; border-spacing: 0px;\n"
3424b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " }\n"
3434b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " td.rowname {\n"
3444b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   text-align:right; font-weight:bold; color:#444444;\n"
3454b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   padding-right:2ex; }\n"
346f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek      "</style>\n</head>\n<body>";
34770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek
348f6f593fae2f0531b4bc06891941f7fbba5217618Ted Kremenek  // Generate header
34944ba7bf54434be88bda7a752f857d42cc92b462dDaniel Dunbar  R.InsertTextBefore(StartLoc, os.str());
350ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  // Generate footer
3511eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
35244ba7bf54434be88bda7a752f857d42cc92b462dDaniel Dunbar  R.InsertTextAfter(EndLoc, "</body></html>\n");
353ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek}
3543245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
3553245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
3563245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// information about keywords, macro expansions etc.  This uses the macro
3573245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// table state from the end of the file, so it won't be perfectly perfect,
3583245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// but it will be reasonably close.
359ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbarvoid html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
3602b2453a7d8fe732561795431f39ceb2b2a832d84Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FID);
3613245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
36205db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  const SourceManager &SM = PP.getSourceManager();
3636e2901407bff59aeb4cc301cc58b034723d0eb49Chris Lattner  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
3644e4d08403ca5cfd4d558fa2936215d3a4e5a528dDavid Blaikie  Lexer L(FID, FromFile, SM, PP.getLangOpts());
365025c3a66402fb713c2d9bf5dc174ff264765379aChris Lattner  const char *BufferStart = L.getBufferStart();
3661eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
3671eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // Inform the preprocessor that we want to retain comments as tokens, so we
3683245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  // can highlight them.
369678c6358c8d4e368c78629099142397c63c1ee35Chris Lattner  L.SetCommentRetentionState(true);
3701eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
371c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
372c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // macros.
3733245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  Token Tok;
374590f0cc643274267d4d41125b62557e1d87886c3Chris Lattner  L.LexFromRawLexer(Tok);
3751eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
37674ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner  while (Tok.isNot(tok::eof)) {
37774ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    // Since we are lexing unexpanded tokens, all tokens are from the main
37874ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    // FileID.
37905db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    unsigned TokOffs = SM.getFileOffset(Tok.getLocation());
3803245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    unsigned TokLen = Tok.getLength();
3813245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    switch (Tok.getKind()) {
382a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    default: break;
383c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara    case tok::identifier:
384c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara      llvm_unreachable("tok::identifier in raw lexing mode!");
385c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara    case tok::raw_identifier: {
386c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara      // Fill in Result.IdentifierInfo and update the token kind,
387c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara      // looking up the identifier in the identifier table.
388c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara      PP.LookUpIdentifierInfo(Tok);
3891eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
390a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // If this is a pp-identifier, for a keyword, highlight it as such.
391c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara      if (Tok.isNot(tok::identifier))
3925ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner        HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
3935ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                       "<span class='keyword'>", "</span>");
394c4586c234edd8df0477a895aebcbc3eb220aed6bChris Lattner      break;
395a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    }
3963245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    case tok::comment:
3975ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner      HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
3985ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                     "<span class='comment'>", "</span>");
3993245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner      break;
4005cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor    case tok::utf8_string_literal:
4015cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor      // Chop off the u part of u8 prefix
4025cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor      ++TokOffs;
4035cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor      --TokLen;
4045cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor      // FALL THROUGH to chop the 8
405cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek    case tok::wide_string_literal:
4065cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor    case tok::utf16_string_literal:
4075cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor    case tok::utf32_string_literal:
4085cee1195584fa8672253139c86e922daeda69b9eDouglas Gregor      // Chop off the L, u, U or 8 prefix
409cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek      ++TokOffs;
410cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek      --TokLen;
411cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek      // FALL THROUGH.
412cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek    case tok::string_literal:
41399831e4677a7e2e051af636221694d60ba31fcdbRichard Smith      // FIXME: Exclude the optional ud-suffix from the highlighted range.
414cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek      HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
415cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek                     "<span class='string_literal'>", "</span>");
416cc1b8532a113fa3216096757b3b4e203867bd5e0Ted Kremenek      break;
4175deb96d06583abb751463427457d46041af262d0Chris Lattner    case tok::hash: {
41874ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      // If this is a preprocessor directive, all tokens to end of line are too.
4195deb96d06583abb751463427457d46041af262d0Chris Lattner      if (!Tok.isAtStartOfLine())
4205deb96d06583abb751463427457d46041af262d0Chris Lattner        break;
4211eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4225deb96d06583abb751463427457d46041af262d0Chris Lattner      // Eat all of the tokens until we get to the next one at the start of
4235deb96d06583abb751463427457d46041af262d0Chris Lattner      // line.
4245deb96d06583abb751463427457d46041af262d0Chris Lattner      unsigned TokEnd = TokOffs+TokLen;
425590f0cc643274267d4d41125b62557e1d87886c3Chris Lattner      L.LexFromRawLexer(Tok);
4265deb96d06583abb751463427457d46041af262d0Chris Lattner      while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
42705db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner        TokEnd = SM.getFileOffset(Tok.getLocation())+Tok.getLength();
428590f0cc643274267d4d41125b62557e1d87886c3Chris Lattner        L.LexFromRawLexer(Tok);
42974ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      }
4301eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4315deb96d06583abb751463427457d46041af262d0Chris Lattner      // Find end of line.  This is a hack.
4325deb96d06583abb751463427457d46041af262d0Chris Lattner      HighlightRange(RB, TokOffs, TokEnd, BufferStart,
4335deb96d06583abb751463427457d46041af262d0Chris Lattner                     "<span class='directive'>", "</span>");
4341eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4355deb96d06583abb751463427457d46041af262d0Chris Lattner      // Don't skip the next token.
4365deb96d06583abb751463427457d46041af262d0Chris Lattner      continue;
4375deb96d06583abb751463427457d46041af262d0Chris Lattner    }
4383245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    }
4391eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
440590f0cc643274267d4d41125b62557e1d87886c3Chris Lattner    L.LexFromRawLexer(Tok);
44174ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner  }
4423245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner}
443c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
444c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// HighlightMacros - This uses the macro table state from the end of the
44505db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner/// file, to re-expand macros and insert (into the HTML) information about the
446c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// macro expansions.  This won't be perfectly perfect, but it will be
447c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// reasonably close.
448ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbarvoid html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
44905db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  // Re-lex the raw token stream into a token buffer.
45005db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  const SourceManager &SM = PP.getSourceManager();
45105db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  std::vector<Token> TokenStream;
4521eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4536e2901407bff59aeb4cc301cc58b034723d0eb49Chris Lattner  const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
4544e4d08403ca5cfd4d558fa2936215d3a4e5a528dDavid Blaikie  Lexer L(FID, FromFile, SM, PP.getLangOpts());
4551eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
45605db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
45705db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  // macros.
45805db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  while (1) {
45905db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    Token Tok;
46005db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    L.LexFromRawLexer(Tok);
4611eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
46205db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    // If this is a # at the start of a line, discard it from the token stream.
46305db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    // We don't want the re-preprocess step to see #defines, #includes or other
46405db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    // preprocessor directives.
46505db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    if (Tok.is(tok::hash) && Tok.isAtStartOfLine())
46605db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner      continue;
467f0b26b1d9dee57c84e55f05200802658a7312683Chris Lattner
468f0b26b1d9dee57c84e55f05200802658a7312683Chris Lattner    // If this is a ## token, change its kind to unknown so that repreprocessing
469f0b26b1d9dee57c84e55f05200802658a7312683Chris Lattner    // it will not produce an error.
470f0b26b1d9dee57c84e55f05200802658a7312683Chris Lattner    if (Tok.is(tok::hashhash))
471f0b26b1d9dee57c84e55f05200802658a7312683Chris Lattner      Tok.setKind(tok::unknown);
4721eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
47305db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    // If this raw token is an identifier, the raw lexer won't have looked up
47405db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    // the corresponding identifier info for it.  Do this now so that it will be
47505db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    // macro expanded when we re-preprocess it.
476c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara    if (Tok.is(tok::raw_identifier))
477c4bf2b9afb7d47445a9dc6bc848657098a4e3851Abramo Bagnara      PP.LookUpIdentifierInfo(Tok);
4781eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
47905db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    TokenStream.push_back(Tok);
4801eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
48105db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner    if (Tok.is(tok::eof)) break;
48205db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  }
4831eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4847c175fb196a2bc3dbc86ea3865c713e1875f3f6dChris Lattner  // Temporarily change the diagnostics object so that we ignore any generated
4857c175fb196a2bc3dbc86ea3865c713e1875f3f6dChris Lattner  // diagnostics from this pass.
486d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  DiagnosticsEngine TmpDiags(PP.getDiagnostics().getDiagnosticIDs(),
487f40c0ac1238a0ef2010238a43cb078465401239aDavid Blaikie                      new IgnoringDiagConsumer);
4881eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
489ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  // FIXME: This is a huge hack; we reuse the input preprocessor because we want
490ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  // its state, but we aren't actually changing it (we hope). This should really
491ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  // construct a copy of the preprocessor.
492ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  Preprocessor &TmpPP = const_cast<Preprocessor&>(PP);
493d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  DiagnosticsEngine *OldDiags = &TmpPP.getDiagnostics();
494ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  TmpPP.setDiagnostics(TmpDiags);
4951eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
496c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Inform the preprocessor that we don't want comments.
497ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  TmpPP.SetCommentRetentionState(false, false);
49805db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner
4996fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose  // We don't want pragmas either. Although we filtered out #pragma, removing
5006fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose  // _Pragma and __pragma is much harder.
5016fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose  bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled();
5026fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose  TmpPP.setPragmasEnabled(false);
5036fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose
50405db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  // Enter the tokens we just lexed.  This will cause them to be macro expanded
50505db4278ecd193edcff63fb8c54818226fceaad2Chris Lattner  // but won't enter sub-files (because we removed #'s).
506ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  TmpPP.EnterTokenStream(&TokenStream[0], TokenStream.size(), false, false);
5071eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
508ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  TokenConcatenation ConcatInfo(TmpPP);
5091eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
510c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Lex all the tokens.
511c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  Token Tok;
512ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  TmpPP.Lex(Tok);
513c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  while (Tok.isNot(tok::eof)) {
514c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Ignore non-macro tokens.
515c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    if (!Tok.getLocation().isMacroID()) {
516ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar      TmpPP.Lex(Tok);
517c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      continue;
518c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    }
5191eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
520b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner    // Okay, we have the first token of a macro expansion: highlight the
5217c5c6764e9c0d5b27b37194fb80aa15cbf78c3e9Chandler Carruth    // expansion by inserting a start tag before the macro expansion and
522b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner    // end tag after it.
523b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner    std::pair<SourceLocation, SourceLocation> LLoc =
524edc3dccece244a584f8ebdb81da6c962c08e79beChandler Carruth      SM.getExpansionRange(Tok.getLocation());
5251eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
526b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner    // Ignore tokens whose instantiation location was not the main file.
527b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner    if (SM.getFileID(LLoc.first) != FID) {
528ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar      TmpPP.Lex(Tok);
529c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      continue;
530c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    }
531b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner
532b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner    assert(SM.getFileID(LLoc.second) == FID &&
533b7949a9a91ad8e49124cd6a82ff98972d7efaadcChris Lattner           "Start and end of expansion must be in the same ultimate file!");
534e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner
535ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar    std::string Expansion = EscapeText(TmpPP.getSpelling(Tok));
5366f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    unsigned LineLen = Expansion.size();
5371eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
5388877321ca66b2887c2f377a7f724a62f34fdf1cdChris Lattner    Token PrevPrevTok;
539867924dbeca06870573fd58d620032da6994b223Chris Lattner    Token PrevTok = Tok;
540c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Okay, eat this token, getting the next one.
541ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar    TmpPP.Lex(Tok);
5421eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
543c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Skip all the rest of the tokens that are part of this macro
544c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // instantiation.  It would be really nice to pop up a window with all the
545c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // spelling of the tokens or something.
546c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    while (!Tok.is(tok::eof) &&
547402785357ab053dd53f4fdd858b9630a5e0f8badChandler Carruth           SM.getExpansionLoc(Tok.getLocation()) == LLoc.first) {
5486f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      // Insert a newline if the macro expansion is getting large.
5496f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      if (LineLen > 60) {
5506f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner        Expansion += "<br>";
5516f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner        LineLen = 0;
5526f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      }
5531eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
5546f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      LineLen -= Expansion.size();
5551eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
556867924dbeca06870573fd58d620032da6994b223Chris Lattner      // If the tokens were already space separated, or if they must be to avoid
557867924dbeca06870573fd58d620032da6994b223Chris Lattner      // them being implicitly pasted, add a space between them.
558867924dbeca06870573fd58d620032da6994b223Chris Lattner      if (Tok.hasLeadingSpace() ||
5598877321ca66b2887c2f377a7f724a62f34fdf1cdChris Lattner          ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok))
560867924dbeca06870573fd58d620032da6994b223Chris Lattner        Expansion += ' ';
5611eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
5629227c6953497be34281354f949d6f4cd34a696ccChris Lattner      // Escape any special characters in the token text.
563ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar      Expansion += EscapeText(TmpPP.getSpelling(Tok));
5646f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      LineLen += Expansion.size();
5651eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
5668877321ca66b2887c2f377a7f724a62f34fdf1cdChris Lattner      PrevPrevTok = PrevTok;
567867924dbeca06870573fd58d620032da6994b223Chris Lattner      PrevTok = Tok;
568ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar      TmpPP.Lex(Tok);
5696f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    }
5701eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
571e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner
572e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner    // Insert the expansion as the end tag, so that multi-line macros all get
573e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner    // highlighted.
574e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner    Expansion = "<span class='expansion'>" + Expansion + "</span></span>";
575e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner
576e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner    HighlightRange(R, LLoc.first, LLoc.second,
577e9e6cb93afa51eee1f83abc7e2cb7d8a0453d810Chris Lattner                   "<span class='macro'>", Expansion.c_str());
578c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  }
5797c175fb196a2bc3dbc86ea3865c713e1875f3f6dChris Lattner
5806fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose  // Restore the preprocessor's old state.
581ff6912b5a9d5073221956fe4d6367b14f3f4b68fDaniel Dunbar  TmpPP.setDiagnostics(*OldDiags);
5826fe6a49c4058211ff4489023c78615ec0266c5ffJordan Rose  TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled);
583c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner}
584