HTMLRewrite.cpp revision 5c176f7a9ba9f7084b903393845be24f85e091da
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"
183245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner#include "clang/Lex/Preprocessor.h"
196a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "clang/Basic/SourceManager.h"
2057df3b950061c73d13d3116f747e79d7955a216aChris Lattner#include "llvm/ADT/SmallString.h"
21339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek#include "llvm/ADT/OwningPtr.h"
226a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include "llvm/Support/MemoryBuffer.h"
236a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek#include <sstream>
246a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenekusing namespace clang;
256a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
269402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
275ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// HighlightRange - Highlight a range in the source code with the specified
285ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// start/end tags.  B/E must be in the same file.  This ensures that
295ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// start/end tags are placed at the start/end of each line if the range is
305ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// multiline.
315ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattnervoid html::HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E,
325ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *StartTag, const char *EndTag) {
335ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  SourceManager &SM = R.getSourceMgr();
345ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  B = SM.getLogicalLoc(B);
355ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  E = SM.getLogicalLoc(E);
365ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  unsigned FileID = SM.getCanonicalFileID(B);
375ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  assert(SM.getCanonicalFileID(E) == FileID && "B/E not in the same file!");
385ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
395ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  unsigned BOffset = SM.getFullFilePos(B);
405ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  unsigned EOffset = SM.getFullFilePos(E);
415ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
425ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  // Include the whole end token in the range.
435ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr());
445ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
455ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  HighlightRange(R.getEditBuffer(FileID), BOffset, EOffset,
465ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                 SM.getBufferData(FileID).first, StartTag, EndTag);
475ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner}
485ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
495ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// HighlightRange - This is the same as the above method, but takes
505ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner/// decomposed file locations.
515ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattnervoid html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
525ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *BufferStart,
535ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                          const char *StartTag, const char *EndTag) {
549402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // Insert the tag at the absolute start/end of the range.
555ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  RB.InsertTextAfter(B, StartTag, strlen(StartTag));
565ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner  RB.InsertTextBefore(E, EndTag, strlen(EndTag));
575ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
589402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // Scan the range to see if there is a \r or \n.  If so, and if the line is
599402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  // not blank, insert tags on that line as well.
609402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  bool HadOpenTag = true;
619402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
629402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  unsigned LastNonWhiteSpace = B;
639402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  for (unsigned i = B; i != E; ++i) {
649402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    switch (BufferStart[i]) {
659402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\r':
669402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\n':
679402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Okay, we found a newline in the range.  If we have an open tag, we need
689402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // to insert a close tag at the first non-whitespace before the newline.
699402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      if (HadOpenTag)
709402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag, strlen(EndTag));
719402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
729402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Instead of inserting an open tag immediately after the newline, we
739402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // wait until we see a non-whitespace character.  This prevents us from
749402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // inserting tags around blank lines, and also allows the open tag to
759402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // be put *after* whitespace on a non-blank line.
769402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      HadOpenTag = false;
779402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
789402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\0':
799402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case ' ':
809402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\t':
819402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\f':
829402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    case '\v':
839402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Ignore whitespace.
849402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
859402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
869402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    default:
879402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // If there is no tag open, do it now.
889402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      if (!HadOpenTag) {
899402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        RB.InsertTextAfter(i, StartTag, strlen(StartTag));
909402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner        HadOpenTag = true;
919402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      }
929402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner
939402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      // Remember this character.
949402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      LastNonWhiteSpace = i;
959402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner      break;
969402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner    }
979402b57a0dca4058fe56d7fd84e97fc496421125Chris Lattner  }
985ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner}
995ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner
100fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenekvoid html::EscapeText(Rewriter& R, unsigned FileID,
101fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenek                      bool EscapeSpaces, bool ReplaceTabs) {
1026a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
1036a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
1046a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const char* C = Buf->getBufferStart();
1056a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
1066a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
1076a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  assert (C <= FileEnd);
1086a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
109735271479ac57c27f744806859efd5b001dea248Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
1105c176f7a9ba9f7084b903393845be24f85e091daChris Lattner
1115c176f7a9ba9f7084b903393845be24f85e091daChris Lattner  unsigned ColNo = 0;
1126a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {
1136a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek    switch (*C) {
1145c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    default: ++ColNo; break;
1155c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    case '\n':
1165c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    case '\r':
1175c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ColNo = 0;
1185c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      break;
119735271479ac57c27f744806859efd5b001dea248Chris Lattner
120735271479ac57c27f744806859efd5b001dea248Chris Lattner    case ' ':
121735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (EscapeSpaces)
122735271479ac57c27f744806859efd5b001dea248Chris Lattner        RB.ReplaceText(FilePos, 1, "&nbsp;", 6);
1235c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
124735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1256a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
1265c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    case '\t': {
127735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (!ReplaceTabs)
12849cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek        break;
1295c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      unsigned NumSpaces = 8-(ColNo&7);
130735271479ac57c27f744806859efd5b001dea248Chris Lattner      if (EscapeSpaces)
1318aa06aca8b2d3771a5405d789b2e704149045dd4Chris Lattner        RB.ReplaceText(FilePos, 1, "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
1325c176f7a9ba9f7084b903393845be24f85e091daChris Lattner                       "&nbsp;&nbsp;&nbsp;", 6*NumSpaces);
133735271479ac57c27f744806859efd5b001dea248Chris Lattner      else
1345c176f7a9ba9f7084b903393845be24f85e091daChris Lattner        RB.ReplaceText(FilePos, 1, "        ", NumSpaces);
1355c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ColNo += NumSpaces;
136735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1375c176f7a9ba9f7084b903393845be24f85e091daChris Lattner    }
138735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '<':
139735271479ac57c27f744806859efd5b001dea248Chris Lattner      RB.ReplaceText(FilePos, 1, "&lt;", 4);
1405c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
141735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
142735271479ac57c27f744806859efd5b001dea248Chris Lattner
143735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '>':
144735271479ac57c27f744806859efd5b001dea248Chris Lattner      RB.ReplaceText(FilePos, 1, "&gt;", 4);
1455c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
146735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
147735271479ac57c27f744806859efd5b001dea248Chris Lattner
148735271479ac57c27f744806859efd5b001dea248Chris Lattner    case '&':
149735271479ac57c27f744806859efd5b001dea248Chris Lattner      RB.ReplaceText(FilePos, 1, "&amp;", 5);
1505c176f7a9ba9f7084b903393845be24f85e091daChris Lattner      ++ColNo;
151735271479ac57c27f744806859efd5b001dea248Chris Lattner      break;
1526a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek    }
1536a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek  }
1546a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek}
1556a34083e9f74a45e2f79c9fab66f177809a5db66Ted Kremenek
156fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenekstd::string html::EscapeText(const std::string& s, bool EscapeSpaces,
157fa5be3617294f0e3c341f0ecb6b2076478b1b5acTed Kremenek                             bool ReplaceTabs) {
158053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
159053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  unsigned len = s.size();
160053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  std::ostringstream os;
161053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
162053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  for (unsigned i = 0 ; i < len; ++i) {
163053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
164053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    char c = s[i];
165053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    switch (c) {
1668570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    default:
1678570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      os << c; break;
1688570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
1698570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    case ' ':
1708570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      if (EscapeSpaces) os << "&nbsp;";
1718570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      else os << ' ';
1728570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      break;
1738570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
1748570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '\t':
1758570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner        if (ReplaceTabs)
1768570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner          for (unsigned i = 0; i < 4; ++i)
1778570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner            os << "&nbsp;";
1788570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner        else
1798570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner          os << c;
1808570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
181053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek        break;
1828570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner
1838570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '<': os << "&lt;"; break;
1848570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '>': os << "&gt;"; break;
1858570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner      case '&': os << "&amp;"; break;
186053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek    }
187053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  }
188053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
189053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek  return os.str();
190053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek}
191053ef593fa9d2b890645a914eee203231fb34458Ted Kremenek
1928570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattnerstatic void AddLineNumber(RewriteBuffer &RB, unsigned LineNo,
1938570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner                          unsigned B, unsigned E) {
19457df3b950061c73d13d3116f747e79d7955a216aChris Lattner  llvm::SmallString<100> Str;
19557df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str += "<tr><td class=\"num\" id=\"LN";
19657df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str.append_uint(LineNo);
19757df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str += "\">";
19857df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str.append_uint(LineNo);
19957df3b950061c73d13d3116f747e79d7955a216aChris Lattner  Str += "</td><td class=\"line\">";
20057df3b950061c73d13d3116f747e79d7955a216aChris Lattner
20149cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek  if (B == E) { // Handle empty lines.
20257df3b950061c73d13d3116f747e79d7955a216aChris Lattner    Str += " </td></tr>";
2038570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    RB.InsertTextBefore(B, &Str[0], Str.size());
20457df3b950061c73d13d3116f747e79d7955a216aChris Lattner  } else {
2058570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    RB.InsertTextBefore(B, &Str[0], Str.size());
2068570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    RB.InsertTextBefore(E, "</td></tr>", strlen("</td></tr>"));
20749cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek  }
208b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek}
209b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
210b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenekvoid html::AddLineNumbers(Rewriter& R, unsigned FileID) {
211b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
212b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
213b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* FileBeg = Buf->getBufferStart();
214b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
215b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  const char* C = FileBeg;
2168570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
217b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
218b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  assert (C <= FileEnd);
219b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
220b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  unsigned LineNo = 0;
221b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  unsigned FilePos = 0;
222b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
223b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek  while (C != FileEnd) {
224b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
225b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    ++LineNo;
226b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    unsigned LineStartPos = FilePos;
227b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    unsigned LineEndPos = FileEnd - FileBeg;
228b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
229b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    assert (FilePos <= LineEndPos);
230b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    assert (C < FileEnd);
231b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
232b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek    // Scan until the newline (or end-of-file).
233b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
23449cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek    while (C != FileEnd) {
23549cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      char c = *C;
23649cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      ++C;
23749cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek
23849cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      if (c == '\n') {
23949cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek        LineEndPos = FilePos++;
240b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek        break;
241b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek      }
24249cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek
24349cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek      ++FilePos;
24449cd6354d5373245dd2e69ca7b7113e6a795d36eTed Kremenek    }
245b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek
2468570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner    AddLineNumber(RB, LineNo, LineStartPos, LineEndPos);
247d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek  }
248d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek
2498570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  // Add one big table tag that surrounds all of the code.
2508570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  RB.InsertTextBefore(0, "<table class=\"code\">\n",
2518570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner                      strlen("<table class=\"code\">\n"));
252d6c1360c2bf234c73572a865f119d0518aca8154Ted Kremenek
2538570f0b0fde7ca812f8d37f52305f3df4dd2ce01Chris Lattner  RB.InsertTextAfter(FileEnd - FileBeg, "</table>", strlen("</table>"));
254b485cd1e0a5a1e942d0e682b9b1c4bc9df111528Ted Kremenek}
255ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
256ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenekvoid html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, unsigned FileID) {
257ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
258ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
259ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const char* FileStart = Buf->getBufferStart();
260ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  const char* FileEnd = Buf->getBufferEnd();
261ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
262ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  SourceLocation StartLoc = SourceLocation::getFileLoc(FileID, 0);
263ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  SourceLocation EndLoc = SourceLocation::getFileLoc(FileID, FileEnd-FileStart);
264ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
265ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  // Generate header
26670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek  R.InsertCStrBefore(StartLoc,
26738941b5b545ba68fa6d723a4123eec8deb7f5e5dTed Kremenek      "<!doctype html>\n" // Use HTML 5 doctype
26838941b5b545ba68fa6d723a4123eec8deb7f5e5dTed Kremenek      "<html>\n<head>\n"
26970bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      "<style type=\"text/css\">\n"
27070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " body { color:#000000; background-color:#ffffff }\n"
27170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " body { font-family:Helvetica, sans-serif; font-size:10pt }\n"
2724b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " h1 { font-size:14pt }\n"
273f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .code { border-collapse:collapse; width:100%; }\n"
27470bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .code { font-family: \"Andale Mono\", monospace; font-size:10pt }\n"
27570bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .code { line-height: 1.2em }\n"
276f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .comment { color: green; font-style: oblique }\n"
277f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .keyword { color: blue }\n"
278f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .directive { color: darkmagenta }\n"
2796f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      // Macro expansions.
28007339a63b46e38c954fcccbef721c609d0c2040eTed Kremenek      " .expansion { display: none; }\n"
28107339a63b46e38c954fcccbef721c609d0c2040eTed Kremenek      " .macro:hover .expansion { display: block; border: 2px solid #FF0000; "
282dc5be47542e6d4a28d20abf9c0f0a0edd72939b6Chris Lattner          "padding: 2px; background-color:#FFF0F0; font-weight: normal; "
2836f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner          "  -webkit-border-radius:5px;  -webkit-box-shadow:1px 1px 7px #000; "
2848aa06aca8b2d3771a5405d789b2e704149045dd4Chris Lattner          "position: absolute; top: -1em; left:10em; z-index: 1 } \n"
285f5016260522e449e9bacdb0e5a87ad3932d9fbd4Ted Kremenek      " .macro { color: darkmagenta; background-color:LemonChiffon;"
2866f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner             // Macros are position: relative to provide base for expansions.
2876f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner             " position: relative }\n"
28870bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { width:2.5em; padding-right:2ex; background-color:#eeeeee }\n"
28970bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { text-align:right; font-size: smaller }\n"
29070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .num { color:#444444 }\n"
29170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .line { padding-left: 1ex; border-left: 3px solid #ccc }\n"
29270bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .line { white-space: pre }\n"
29370bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { background-color:#fff8b4; color:#000000 }\n"
29470bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { -webkit-box-shadow:1px 1px 7px #000 }\n"
29570bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { -webkit-border-radius:5px }\n"
29670bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { font-family:Helvetica, sans-serif; font-size: smaller }\n"
29770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { font-weight: bold }\n"
29870bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { float:left }\n"
29970bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { padding:0.5em 1ex 0.5em 1ex }\n"
30070bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .msg { margin-top:10px; margin-bottom:10px }\n"
30170bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .mrange { background-color:#dfddf3 }\n"
30270bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .mrange { border-bottom:1px solid #6F9DBE }\n"
30370bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      " .PathIndex { font-weight: bold }\n"
3044b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " table.simpletable {\n"
3054b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   padding: 5px;\n"
3064b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   font-size:12pt;\n"
3074b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   margin:20px;\n"
3084b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   border-collapse: collapse; border-spacing: 0px;\n"
3094b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " }\n"
3104b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      " td.rowname {\n"
3114b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   text-align:right; font-weight:bold; color:#444444;\n"
3124b0f81323b518429203051bbcd4864bbf4b000a9Ted Kremenek      "   padding-right:2ex; }\n"
31370bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek      "</style>\n</head>\n<body>");
31470bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek
315ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek  // Generate footer
316ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek
31770bcba6030a76edf46c4f941ad9a5297a1f98c47Ted Kremenek  R.InsertCStrAfter(EndLoc, "</body></html>\n");
318ad0a203130dc5d1fb7231b88767174511424fa98Ted Kremenek}
3193245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
3203245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
3213245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// information about keywords, macro expansions etc.  This uses the macro
3223245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// table state from the end of the file, so it won't be perfectly perfect,
3233245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner/// but it will be reasonably close.
3243245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattnervoid html::SyntaxHighlight(Rewriter &R, unsigned FileID, Preprocessor &PP) {
3253245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
3263245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
327a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  const SourceManager &SourceMgr = PP.getSourceManager();
328a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  std::pair<const char*, const char*> File = SourceMgr.getBufferData(FileID);
329a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  const char *BufferStart = File.first;
330a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner
331a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  Lexer L(SourceLocation::getFileLoc(FileID, 0), PP.getLangOptions(),
332a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner          File.first, File.second);
333a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner
3343245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  // Inform the preprocessor that we want to retain comments as tokens, so we
3353245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  // can highlight them.
336678c6358c8d4e368c78629099142397c63c1ee35Chris Lattner  L.SetCommentRetentionState(true);
3373245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
338c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Lex all the tokens in raw mode, to avoid entering #includes or expanding
339c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // macros.
3403245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner  Token Tok;
341a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner  L.LexRawToken(Tok);
34274ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner
34374ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner  while (Tok.isNot(tok::eof)) {
34474ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    // Since we are lexing unexpanded tokens, all tokens are from the main
34574ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    // FileID.
34674ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner    unsigned TokOffs = SourceMgr.getFullFilePos(Tok.getLocation());
3473245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    unsigned TokLen = Tok.getLength();
3483245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    switch (Tok.getKind()) {
349a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    default: break;
350a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    case tok::identifier: {
351a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // Fill in Result.IdentifierInfo, looking up the identifier in the
352a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // identifier table.
353a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      IdentifierInfo *II = PP.LookUpIdentifierInfo(Tok, BufferStart+TokOffs);
354a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner
355a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner      // If this is a pp-identifier, for a keyword, highlight it as such.
3565ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner      if (II->getTokenID() != tok::identifier)
3575ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner        HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
3585ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                       "<span class='keyword'>", "</span>");
359c4586c234edd8df0477a895aebcbc3eb220aed6bChris Lattner      break;
360a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    }
3613245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    case tok::comment:
3625ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner      HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
3635ef3e2c45f13fccdb0d7bbcf24c1beee8eee6f64Chris Lattner                     "<span class='comment'>", "</span>");
3643245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner      break;
3655deb96d06583abb751463427457d46041af262d0Chris Lattner    case tok::hash: {
36674ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      // If this is a preprocessor directive, all tokens to end of line are too.
3675deb96d06583abb751463427457d46041af262d0Chris Lattner      if (!Tok.isAtStartOfLine())
3685deb96d06583abb751463427457d46041af262d0Chris Lattner        break;
3695deb96d06583abb751463427457d46041af262d0Chris Lattner
3705deb96d06583abb751463427457d46041af262d0Chris Lattner      // Eat all of the tokens until we get to the next one at the start of
3715deb96d06583abb751463427457d46041af262d0Chris Lattner      // line.
3725deb96d06583abb751463427457d46041af262d0Chris Lattner      unsigned TokEnd = TokOffs+TokLen;
3735deb96d06583abb751463427457d46041af262d0Chris Lattner      L.LexRawToken(Tok);
3745deb96d06583abb751463427457d46041af262d0Chris Lattner      while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
3755deb96d06583abb751463427457d46041af262d0Chris Lattner        TokEnd = SourceMgr.getFullFilePos(Tok.getLocation())+Tok.getLength();
3765deb96d06583abb751463427457d46041af262d0Chris Lattner        L.LexRawToken(Tok);
37774ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner      }
3785deb96d06583abb751463427457d46041af262d0Chris Lattner
3795deb96d06583abb751463427457d46041af262d0Chris Lattner      // Find end of line.  This is a hack.
3805deb96d06583abb751463427457d46041af262d0Chris Lattner      HighlightRange(RB, TokOffs, TokEnd, BufferStart,
3815deb96d06583abb751463427457d46041af262d0Chris Lattner                     "<span class='directive'>", "</span>");
3825deb96d06583abb751463427457d46041af262d0Chris Lattner
3835deb96d06583abb751463427457d46041af262d0Chris Lattner      // Don't skip the next token.
3845deb96d06583abb751463427457d46041af262d0Chris Lattner      continue;
3855deb96d06583abb751463427457d46041af262d0Chris Lattner    }
3863245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner    }
3873245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner
388a745e8c52839d9c8dd0fd8d5276b4eab182ec7f2Chris Lattner    L.LexRawToken(Tok);
38974ea3e5c57c087c046223096a97ea4e365f85eb6Chris Lattner  }
3903245a0a1c7a4fd74fca845b2edba275bb126d773Chris Lattner}
391c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
392c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// HighlightMacros - This uses the macro table state from the end of the
393c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// file, to reexpand macros and insert (into the HTML) information about the
394c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// macro expansions.  This won't be perfectly perfect, but it will be
395c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner/// reasonably close.
396339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenekvoid html::HighlightMacros(Rewriter &R, unsigned FileID,
397339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek                           PreprocessorFactory &PPF) {
398339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek
399339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek  llvm::OwningPtr<Preprocessor> PP(PPF.CreatePreprocessor());
400339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek
401339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek
402c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  RewriteBuffer &RB = R.getEditBuffer(FileID);
403c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
404c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Inform the preprocessor that we don't want comments.
405339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek  PP->SetCommentRetentionState(false, false);
406c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
407c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Start parsing the specified input file.
408339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek  PP->EnterMainSourceFile();
409c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
410c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  // Lex all the tokens.
411339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek  const SourceManager &SourceMgr = PP->getSourceManager();
412c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  Token Tok;
413339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek  PP->Lex(Tok);
414c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  while (Tok.isNot(tok::eof)) {
415c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Ignore non-macro tokens.
416c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    if (!Tok.getLocation().isMacroID()) {
417339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek      PP->Lex(Tok);
418c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      continue;
419c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    }
420c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
421c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Ignore tokens whose logical location was not the main file.
422c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    SourceLocation LLoc = SourceMgr.getLogicalLoc(Tok.getLocation());
423c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    std::pair<unsigned, unsigned> LLocInfo =
424c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      SourceMgr.getDecomposedFileLoc(LLoc);
425c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
426c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    if (LLocInfo.first != FileID) {
427339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek      PP->Lex(Tok);
428c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner      continue;
429c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    }
430c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
431c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Okay, we have the first token of a macro expansion: highlight the
432c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // instantiation.
433c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
434c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Get the size of current macro call itself.
435c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // FIXME: This should highlight the args of a function-like
436c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // macro, using a heuristic.
437c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    unsigned TokLen = Lexer::MeasureTokenLength(LLoc, SourceMgr);
438c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
439c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    unsigned TokOffs = LLocInfo.second;
4406f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    // Highlight the macro invocation itself.
441c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    RB.InsertTextAfter(TokOffs, "<span class='macro'>",
442c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner                       strlen("<span class='macro'>"));
443c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    RB.InsertTextBefore(TokOffs+TokLen, "</span>", strlen("</span>"));
444c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
445339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek    std::string Expansion = PP->getSpelling(Tok);
4466f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    unsigned LineLen = Expansion.size();
4476f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner
448c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Okay, eat this token, getting the next one.
449339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek    PP->Lex(Tok);
450c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
451c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // Skip all the rest of the tokens that are part of this macro
452c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // instantiation.  It would be really nice to pop up a window with all the
453c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    // spelling of the tokens or something.
454c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner    while (!Tok.is(tok::eof) &&
4556f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner           SourceMgr.getLogicalLoc(Tok.getLocation()) == LLoc) {
4566f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      // Insert a newline if the macro expansion is getting large.
4576f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      if (LineLen > 60) {
4586f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner        Expansion += "<br>";
4596f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner        LineLen = 0;
4606f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      }
4616f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner
4626f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      LineLen -= Expansion.size();
4639227c6953497be34281354f949d6f4cd34a696ccChris Lattner      // Escape any special characters in the token text.
4649227c6953497be34281354f949d6f4cd34a696ccChris Lattner      Expansion += ' ' + EscapeText(PP->getSpelling(Tok));
4656f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner      LineLen += Expansion.size();
466339b9c27759d7b6a53e2370f83f66e78b3254595Ted Kremenek      PP->Lex(Tok);
4676f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    }
4689227c6953497be34281354f949d6f4cd34a696ccChris Lattner
4696f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    // Insert the information about the expansion inside the macro span.
4706f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    Expansion = "<span class='expansion'>" + Expansion + "</span>";
4716f46be279f4bc8fc24611f060258bcfbe1c175c4Chris Lattner    RB.InsertTextBefore(TokOffs+TokLen, Expansion.c_str(), Expansion.size());
472c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner  }
473c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner}
474c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
475c54d50a4180520370c12dd7d06d035263d357d56Chris Lattner
476