clang 19.0.0git
HTMLRewrite.cpp
Go to the documentation of this file.
1//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the HTMLRewriter class, which is used to translate the
10// text of a source file into prettified HTML.
11//
12//===----------------------------------------------------------------------===//
13
19#include "llvm/ADT/SmallString.h"
20#include "llvm/Support/ErrorHandling.h"
21#include "llvm/Support/MemoryBuffer.h"
22#include "llvm/Support/raw_ostream.h"
23#include <memory>
24
25using namespace clang;
26using namespace llvm;
27using namespace html;
28
29/// HighlightRange - Highlight a range in the source code with the specified
30/// start/end tags. B/E must be in the same file. This ensures that
31/// start/end tags are placed at the start/end of each line if the range is
32/// multiline.
34 const char *StartTag, const char *EndTag,
35 bool IsTokenRange) {
37 B = SM.getExpansionLoc(B);
38 E = SM.getExpansionLoc(E);
39 FileID FID = SM.getFileID(B);
40 assert(SM.getFileID(E) == FID && "B/E not in the same file!");
41
42 unsigned BOffset = SM.getFileOffset(B);
43 unsigned EOffset = SM.getFileOffset(E);
44
45 // Include the whole end token in the range.
46 if (IsTokenRange)
48
49 bool Invalid = false;
50 const char *BufferStart = SM.getBufferData(FID, &Invalid).data();
51 if (Invalid)
52 return;
53
54 HighlightRange(R.getEditBuffer(FID), BOffset, EOffset,
55 BufferStart, StartTag, EndTag);
56}
57
58/// HighlightRange - This is the same as the above method, but takes
59/// decomposed file locations.
60void html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
61 const char *BufferStart,
62 const char *StartTag, const char *EndTag) {
63 // Insert the tag at the absolute start/end of the range.
64 RB.InsertTextAfter(B, StartTag);
65 RB.InsertTextBefore(E, EndTag);
66
67 // Scan the range to see if there is a \r or \n. If so, and if the line is
68 // not blank, insert tags on that line as well.
69 bool HadOpenTag = true;
70
71 unsigned LastNonWhiteSpace = B;
72 for (unsigned i = B; i != E; ++i) {
73 switch (BufferStart[i]) {
74 case '\r':
75 case '\n':
76 // Okay, we found a newline in the range. If we have an open tag, we need
77 // to insert a close tag at the first non-whitespace before the newline.
78 if (HadOpenTag)
79 RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag);
80
81 // Instead of inserting an open tag immediately after the newline, we
82 // wait until we see a non-whitespace character. This prevents us from
83 // inserting tags around blank lines, and also allows the open tag to
84 // be put *after* whitespace on a non-blank line.
85 HadOpenTag = false;
86 break;
87 case '\0':
88 case ' ':
89 case '\t':
90 case '\f':
91 case '\v':
92 // Ignore whitespace.
93 break;
94
95 default:
96 // If there is no tag open, do it now.
97 if (!HadOpenTag) {
98 RB.InsertTextAfter(i, StartTag);
99 HadOpenTag = true;
100 }
101
102 // Remember this character.
103 LastNonWhiteSpace = i;
104 break;
105 }
106 }
107}
108
109namespace clang::html {
111 // These structs mimic input arguments of HighlightRange().
112 struct Highlight {
114 std::string StartTag, EndTag;
116 };
118 unsigned B, E;
119 std::string StartTag, EndTag;
120 };
121
122 // SmallVector isn't appropriate because these vectors are almost never small.
123 using HighlightList = std::vector<Highlight>;
124 using RawHighlightList = std::vector<RawHighlight>;
125
126 DenseMap<FileID, RawHighlightList> SyntaxHighlights;
127 DenseMap<FileID, HighlightList> MacroHighlights;
128};
129} // namespace clang::html
130
132 return std::make_shared<RelexRewriteCache>();
133}
134
136 bool EscapeSpaces, bool ReplaceTabs) {
137
138 llvm::MemoryBufferRef Buf = R.getSourceMgr().getBufferOrFake(FID);
139 const char* C = Buf.getBufferStart();
140 const char* FileEnd = Buf.getBufferEnd();
141
142 assert (C <= FileEnd);
143
144 RewriteBuffer &RB = R.getEditBuffer(FID);
145
146 unsigned ColNo = 0;
147 for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {
148 switch (*C) {
149 default: ++ColNo; break;
150 case '\n':
151 case '\r':
152 ColNo = 0;
153 break;
154
155 case ' ':
156 if (EscapeSpaces)
157 RB.ReplaceText(FilePos, 1, "&nbsp;");
158 ++ColNo;
159 break;
160 case '\f':
161 RB.ReplaceText(FilePos, 1, "<hr>");
162 ColNo = 0;
163 break;
164
165 case '\t': {
166 if (!ReplaceTabs)
167 break;
168 unsigned NumSpaces = 8-(ColNo&7);
169 if (EscapeSpaces)
170 RB.ReplaceText(FilePos, 1,
171 StringRef("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
172 "&nbsp;&nbsp;&nbsp;", 6*NumSpaces));
173 else
174 RB.ReplaceText(FilePos, 1, StringRef(" ", NumSpaces));
175 ColNo += NumSpaces;
176 break;
177 }
178 case '<':
179 RB.ReplaceText(FilePos, 1, "&lt;");
180 ++ColNo;
181 break;
182
183 case '>':
184 RB.ReplaceText(FilePos, 1, "&gt;");
185 ++ColNo;
186 break;
187
188 case '&':
189 RB.ReplaceText(FilePos, 1, "&amp;");
190 ++ColNo;
191 break;
192 }
193 }
194}
195
196std::string html::EscapeText(StringRef s, bool EscapeSpaces, bool ReplaceTabs) {
197
198 unsigned len = s.size();
199 std::string Str;
200 llvm::raw_string_ostream os(Str);
201
202 for (unsigned i = 0 ; i < len; ++i) {
203
204 char c = s[i];
205 switch (c) {
206 default:
207 os << c; break;
208
209 case ' ':
210 if (EscapeSpaces) os << "&nbsp;";
211 else os << ' ';
212 break;
213
214 case '\t':
215 if (ReplaceTabs) {
216 if (EscapeSpaces)
217 for (unsigned i = 0; i < 4; ++i)
218 os << "&nbsp;";
219 else
220 for (unsigned i = 0; i < 4; ++i)
221 os << " ";
222 }
223 else
224 os << c;
225
226 break;
227
228 case '<': os << "&lt;"; break;
229 case '>': os << "&gt;"; break;
230 case '&': os << "&amp;"; break;
231 }
232 }
233
234 return Str;
235}
236
237static void AddLineNumber(RewriteBuffer &RB, unsigned LineNo,
238 unsigned B, unsigned E) {
240 llvm::raw_svector_ostream OS(Str);
241
242 OS << "<tr class=\"codeline\" data-linenumber=\"" << LineNo << "\">"
243 << "<td class=\"num\" id=\"LN" << LineNo << "\">" << LineNo
244 << "</td><td class=\"line\">";
245
246 if (B == E) { // Handle empty lines.
247 OS << " </td></tr>";
248 RB.InsertTextBefore(B, OS.str());
249 } else {
250 RB.InsertTextBefore(B, OS.str());
251 RB.InsertTextBefore(E, "</td></tr>");
252 }
253}
254
256
257 llvm::MemoryBufferRef Buf = R.getSourceMgr().getBufferOrFake(FID);
258 const char* FileBeg = Buf.getBufferStart();
259 const char* FileEnd = Buf.getBufferEnd();
260 const char* C = FileBeg;
261 RewriteBuffer &RB = R.getEditBuffer(FID);
262
263 assert (C <= FileEnd);
264
265 unsigned LineNo = 0;
266 unsigned FilePos = 0;
267
268 while (C != FileEnd) {
269
270 ++LineNo;
271 unsigned LineStartPos = FilePos;
272 unsigned LineEndPos = FileEnd - FileBeg;
273
274 assert (FilePos <= LineEndPos);
275 assert (C < FileEnd);
276
277 // Scan until the newline (or end-of-file).
278
279 while (C != FileEnd) {
280 char c = *C;
281 ++C;
282
283 if (c == '\n') {
284 LineEndPos = FilePos++;
285 break;
286 }
287
288 ++FilePos;
289 }
290
291 AddLineNumber(RB, LineNo, LineStartPos, LineEndPos);
292 }
293
294 // Add one big table tag that surrounds all of the code.
295 std::string s;
296 llvm::raw_string_ostream os(s);
297 os << "<table class=\"code\" data-fileid=\"" << FID.getHashValue() << "\">\n";
298 RB.InsertTextBefore(0, os.str());
299 RB.InsertTextAfter(FileEnd - FileBeg, "</table>");
300}
301
303 StringRef title) {
304
305 llvm::MemoryBufferRef Buf = R.getSourceMgr().getBufferOrFake(FID);
306 const char* FileStart = Buf.getBufferStart();
307 const char* FileEnd = Buf.getBufferEnd();
308
310 SourceLocation EndLoc = StartLoc.getLocWithOffset(FileEnd-FileStart);
311
312 std::string s;
313 llvm::raw_string_ostream os(s);
314 os << "<!doctype html>\n" // Use HTML 5 doctype
315 "<html>\n<head>\n";
316
317 if (!title.empty())
318 os << "<title>" << html::EscapeText(title) << "</title>\n";
319
320 os << R"<<<(
321<style type="text/css">
322body { color:#000000; background-color:#ffffff }
323body { font-family:Helvetica, sans-serif; font-size:10pt }
324h1 { font-size:14pt }
325.FileName { margin-top: 5px; margin-bottom: 5px; display: inline; }
326.FileNav { margin-left: 5px; margin-right: 5px; display: inline; }
327.FileNav a { text-decoration:none; font-size: larger; }
328.divider { margin-top: 30px; margin-bottom: 30px; height: 15px; }
329.divider { background-color: gray; }
330.code { border-collapse:collapse; width:100%; }
331.code { font-family: "Monospace", monospace; font-size:10pt }
332.code { line-height: 1.2em }
333.comment { color: green; font-style: oblique }
334.keyword { color: blue }
335.string_literal { color: red }
336.directive { color: darkmagenta }
337
338/* Macros and variables could have pop-up notes hidden by default.
339 - Macro pop-up: expansion of the macro
340 - Variable pop-up: value (table) of the variable */
341.macro_popup, .variable_popup { display: none; }
342
343/* Pop-up appears on mouse-hover event. */
344.macro:hover .macro_popup, .variable:hover .variable_popup {
345 display: block;
346 padding: 2px;
347 -webkit-border-radius:5px;
348 -webkit-box-shadow:1px 1px 7px #000;
349 border-radius:5px;
350 box-shadow:1px 1px 7px #000;
351 position: absolute;
352 top: -1em;
353 left:10em;
354 z-index: 1
355}
356
357.macro_popup {
358 border: 2px solid red;
359 background-color:#FFF0F0;
360 font-weight: normal;
361}
362
363.variable_popup {
364 border: 2px solid blue;
365 background-color:#F0F0FF;
366 font-weight: bold;
367 font-family: Helvetica, sans-serif;
368 font-size: 9pt;
369}
370
371/* Pop-up notes needs a relative position as a base where they pops up. */
372.macro, .variable {
373 background-color: PaleGoldenRod;
374 position: relative;
375}
376.macro { color: DarkMagenta; }
377
378#tooltiphint {
379 position: fixed;
380 width: 50em;
381 margin-left: -25em;
382 left: 50%;
383 padding: 10px;
384 border: 1px solid #b0b0b0;
385 border-radius: 2px;
386 box-shadow: 1px 1px 7px black;
387 background-color: #c0c0c0;
388 z-index: 2;
389}
390
391.num { width:2.5em; padding-right:2ex; background-color:#eeeeee }
392.num { text-align:right; font-size:8pt }
393.num { color:#444444 }
394.line { padding-left: 1ex; border-left: 3px solid #ccc }
395.line { white-space: pre }
396.msg { -webkit-box-shadow:1px 1px 7px #000 }
397.msg { box-shadow:1px 1px 7px #000 }
398.msg { -webkit-border-radius:5px }
399.msg { border-radius:5px }
400.msg { font-family:Helvetica, sans-serif; font-size:8pt }
401.msg { float:left }
402.msg { position:relative }
403.msg { padding:0.25em 1ex 0.25em 1ex }
404.msg { margin-top:10px; margin-bottom:10px }
405.msg { font-weight:bold }
406.msg { max-width:60em; word-wrap: break-word; white-space: pre-wrap }
407.msgT { padding:0x; spacing:0x }
408.msgEvent { background-color:#fff8b4; color:#000000 }
409.msgControl { background-color:#bbbbbb; color:#000000 }
410.msgNote { background-color:#ddeeff; color:#000000 }
411.mrange { background-color:#dfddf3 }
412.mrange { border-bottom:1px solid #6F9DBE }
413.PathIndex { font-weight: bold; padding:0px 5px; margin-right:5px; }
414.PathIndex { -webkit-border-radius:8px }
415.PathIndex { border-radius:8px }
416.PathIndexEvent { background-color:#bfba87 }
417.PathIndexControl { background-color:#8c8c8c }
418.PathIndexPopUp { background-color: #879abc; }
419.PathNav a { text-decoration:none; font-size: larger }
420.CodeInsertionHint { font-weight: bold; background-color: #10dd10 }
421.CodeRemovalHint { background-color:#de1010 }
422.CodeRemovalHint { border-bottom:1px solid #6F9DBE }
423.msg.selected{ background-color:orange !important; }
424
425table.simpletable {
426 padding: 5px;
427 font-size:12pt;
428 margin:20px;
429 border-collapse: collapse; border-spacing: 0px;
430}
431td.rowname {
432 text-align: right;
433 vertical-align: top;
434 font-weight: bold;
435 color:#444444;
436 padding-right:2ex;
437}
438
439/* Hidden text. */
440input.spoilerhider + label {
441 cursor: pointer;
442 text-decoration: underline;
443 display: block;
444}
445input.spoilerhider {
446 display: none;
447}
448input.spoilerhider ~ .spoiler {
449 overflow: hidden;
450 margin: 10px auto 0;
451 height: 0;
452 opacity: 0;
453}
454input.spoilerhider:checked + label + .spoiler{
455 height: auto;
456 opacity: 1;
457}
458</style>
459</head>
460<body>)<<<";
461
462 // Generate header
463 R.InsertTextBefore(StartLoc, os.str());
464 // Generate footer
465
466 R.InsertTextAfter(EndLoc, "</body></html>\n");
467}
468
469/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
470/// information about keywords, macro expansions etc. This uses the macro
471/// table state from the end of the file, so it won't be perfectly perfect,
472/// but it will be reasonably close.
473static void SyntaxHighlightImpl(
474 Rewriter &R, FileID FID, const Preprocessor &PP,
475 llvm::function_ref<void(RewriteBuffer &, unsigned, unsigned, const char *,
476 const char *, const char *)>
477 HighlightRangeCallback) {
478
479 RewriteBuffer &RB = R.getEditBuffer(FID);
480 const SourceManager &SM = PP.getSourceManager();
481 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
482 const char *BufferStart = FromFile.getBuffer().data();
483
484 Lexer L(FID, FromFile, SM, PP.getLangOpts());
485
486 // Inform the preprocessor that we want to retain comments as tokens, so we
487 // can highlight them.
489
490 // Lex all the tokens in raw mode, to avoid entering #includes or expanding
491 // macros.
492 Token Tok;
493 L.LexFromRawLexer(Tok);
494
495 while (Tok.isNot(tok::eof)) {
496 // Since we are lexing unexpanded tokens, all tokens are from the main
497 // FileID.
498 unsigned TokOffs = SM.getFileOffset(Tok.getLocation());
499 unsigned TokLen = Tok.getLength();
500 switch (Tok.getKind()) {
501 default: break;
502 case tok::identifier:
503 llvm_unreachable("tok::identifier in raw lexing mode!");
504 case tok::raw_identifier: {
505 // Fill in Result.IdentifierInfo and update the token kind,
506 // looking up the identifier in the identifier table.
507 PP.LookUpIdentifierInfo(Tok);
508
509 // If this is a pp-identifier, for a keyword, highlight it as such.
510 if (Tok.isNot(tok::identifier))
511 HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
512 "<span class='keyword'>", "</span>");
513 break;
514 }
515 case tok::comment:
516 HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
517 "<span class='comment'>", "</span>");
518 break;
519 case tok::utf8_string_literal:
520 // Chop off the u part of u8 prefix
521 ++TokOffs;
522 --TokLen;
523 // FALL THROUGH to chop the 8
524 [[fallthrough]];
525 case tok::wide_string_literal:
526 case tok::utf16_string_literal:
527 case tok::utf32_string_literal:
528 // Chop off the L, u, U or 8 prefix
529 ++TokOffs;
530 --TokLen;
531 [[fallthrough]];
532 case tok::string_literal:
533 // FIXME: Exclude the optional ud-suffix from the highlighted range.
534 HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
535 "<span class='string_literal'>", "</span>");
536 break;
537 case tok::hash: {
538 // If this is a preprocessor directive, all tokens to end of line are too.
539 if (!Tok.isAtStartOfLine())
540 break;
541
542 // Eat all of the tokens until we get to the next one at the start of
543 // line.
544 unsigned TokEnd = TokOffs+TokLen;
545 L.LexFromRawLexer(Tok);
546 while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
547 TokEnd = SM.getFileOffset(Tok.getLocation())+Tok.getLength();
548 L.LexFromRawLexer(Tok);
549 }
550
551 // Find end of line. This is a hack.
552 HighlightRangeCallback(RB, TokOffs, TokEnd, BufferStart,
553 "<span class='directive'>", "</span>");
554
555 // Don't skip the next token.
556 continue;
557 }
558 }
559
560 L.LexFromRawLexer(Tok);
561 }
562}
563void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP,
565 RewriteBuffer &RB = R.getEditBuffer(FID);
566 const SourceManager &SM = PP.getSourceManager();
567 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
568 const char *BufferStart = FromFile.getBuffer().data();
569
570 if (Cache) {
571 auto CacheIt = Cache->SyntaxHighlights.find(FID);
572 if (CacheIt != Cache->SyntaxHighlights.end()) {
573 for (const RelexRewriteCache::RawHighlight &H : CacheIt->second) {
574 HighlightRange(RB, H.B, H.E, BufferStart, H.StartTag.data(),
575 H.EndTag.data());
576 }
577 return;
578 }
579 }
580
581 // "Every time you would call HighlightRange, cache the inputs as well."
582 auto HighlightRangeCallback = [&](RewriteBuffer &RB, unsigned B, unsigned E,
583 const char *BufferStart,
584 const char *StartTag, const char *EndTag) {
585 HighlightRange(RB, B, E, BufferStart, StartTag, EndTag);
586
587 if (Cache)
588 Cache->SyntaxHighlights[FID].push_back({B, E, StartTag, EndTag});
589 };
590
591 SyntaxHighlightImpl(R, FID, PP, HighlightRangeCallback);
592}
593
594static void HighlightMacrosImpl(
595 Rewriter &R, FileID FID, const Preprocessor &PP,
596 llvm::function_ref<void(Rewriter &, SourceLocation, SourceLocation,
597 const char *, const char *, bool)>
598 HighlightRangeCallback) {
599
600 // Re-lex the raw token stream into a token buffer.
601 const SourceManager &SM = PP.getSourceManager();
602 std::vector<Token> TokenStream;
603
604 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
605 Lexer L(FID, FromFile, SM, PP.getLangOpts());
606
607 // Lex all the tokens in raw mode, to avoid entering #includes or expanding
608 // macros.
609 while (true) {
610 Token Tok;
611 L.LexFromRawLexer(Tok);
612
613 // If this is a # at the start of a line, discard it from the token stream.
614 // We don't want the re-preprocess step to see #defines, #includes or other
615 // preprocessor directives.
616 if (Tok.is(tok::hash) && Tok.isAtStartOfLine())
617 continue;
618
619 // If this is a ## token, change its kind to unknown so that repreprocessing
620 // it will not produce an error.
621 if (Tok.is(tok::hashhash))
622 Tok.setKind(tok::unknown);
623
624 // If this raw token is an identifier, the raw lexer won't have looked up
625 // the corresponding identifier info for it. Do this now so that it will be
626 // macro expanded when we re-preprocess it.
627 if (Tok.is(tok::raw_identifier))
628 PP.LookUpIdentifierInfo(Tok);
629
630 TokenStream.push_back(Tok);
631
632 if (Tok.is(tok::eof)) break;
633 }
634
635 // Temporarily change the diagnostics object so that we ignore any generated
636 // diagnostics from this pass.
640
641 // FIXME: This is a huge hack; we reuse the input preprocessor because we want
642 // its state, but we aren't actually changing it (we hope). This should really
643 // construct a copy of the preprocessor.
644 Preprocessor &TmpPP = const_cast<Preprocessor&>(PP);
645 DiagnosticsEngine *OldDiags = &TmpPP.getDiagnostics();
646 TmpPP.setDiagnostics(TmpDiags);
647
648 // Inform the preprocessor that we don't want comments.
649 TmpPP.SetCommentRetentionState(false, false);
650
651 // We don't want pragmas either. Although we filtered out #pragma, removing
652 // _Pragma and __pragma is much harder.
653 bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled();
654 TmpPP.setPragmasEnabled(false);
655
656 // Enter the tokens we just lexed. This will cause them to be macro expanded
657 // but won't enter sub-files (because we removed #'s).
658 TmpPP.EnterTokenStream(TokenStream, false, /*IsReinject=*/false);
659
660 TokenConcatenation ConcatInfo(TmpPP);
661
662 // Lex all the tokens.
663 Token Tok;
664 TmpPP.Lex(Tok);
665 while (Tok.isNot(tok::eof)) {
666 // Ignore non-macro tokens.
667 if (!Tok.getLocation().isMacroID()) {
668 TmpPP.Lex(Tok);
669 continue;
670 }
671
672 // Okay, we have the first token of a macro expansion: highlight the
673 // expansion by inserting a start tag before the macro expansion and
674 // end tag after it.
675 CharSourceRange LLoc = SM.getExpansionRange(Tok.getLocation());
676
677 // Ignore tokens whose instantiation location was not the main file.
678 if (SM.getFileID(LLoc.getBegin()) != FID) {
679 TmpPP.Lex(Tok);
680 continue;
681 }
682
683 assert(SM.getFileID(LLoc.getEnd()) == FID &&
684 "Start and end of expansion must be in the same ultimate file!");
685
686 std::string Expansion = EscapeText(TmpPP.getSpelling(Tok));
687 unsigned LineLen = Expansion.size();
688
689 Token PrevPrevTok;
690 Token PrevTok = Tok;
691 // Okay, eat this token, getting the next one.
692 TmpPP.Lex(Tok);
693
694 // Skip all the rest of the tokens that are part of this macro
695 // instantiation. It would be really nice to pop up a window with all the
696 // spelling of the tokens or something.
697 while (!Tok.is(tok::eof) &&
698 SM.getExpansionLoc(Tok.getLocation()) == LLoc.getBegin()) {
699 // Insert a newline if the macro expansion is getting large.
700 if (LineLen > 60) {
701 Expansion += "<br>";
702 LineLen = 0;
703 }
704
705 LineLen -= Expansion.size();
706
707 // If the tokens were already space separated, or if they must be to avoid
708 // them being implicitly pasted, add a space between them.
709 if (Tok.hasLeadingSpace() ||
710 ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok))
711 Expansion += ' ';
712
713 // Escape any special characters in the token text.
714 Expansion += EscapeText(TmpPP.getSpelling(Tok));
715 LineLen += Expansion.size();
716
717 PrevPrevTok = PrevTok;
718 PrevTok = Tok;
719 TmpPP.Lex(Tok);
720 }
721
722 // Insert the 'macro_popup' as the end tag, so that multi-line macros all
723 // get highlighted.
724 Expansion = "<span class='macro_popup'>" + Expansion + "</span></span>";
725
726 HighlightRangeCallback(R, LLoc.getBegin(), LLoc.getEnd(),
727 "<span class='macro'>", Expansion.c_str(),
728 LLoc.isTokenRange());
729 }
730
731 // Restore the preprocessor's old state.
732 TmpPP.setDiagnostics(*OldDiags);
733 TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled);
734}
735
736/// HighlightMacros - This uses the macro table state from the end of the
737/// file, to re-expand macros and insert (into the HTML) information about the
738/// macro expansions. This won't be perfectly perfect, but it will be
739/// reasonably close.
740void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP,
742 if (Cache) {
743 auto CacheIt = Cache->MacroHighlights.find(FID);
744 if (CacheIt != Cache->MacroHighlights.end()) {
745 for (const RelexRewriteCache::Highlight &H : CacheIt->second) {
746 HighlightRange(R, H.B, H.E, H.StartTag.data(), H.EndTag.data(),
747 H.IsTokenRange);
748 }
749 return;
750 }
751 }
752
753 // "Every time you would call HighlightRange, cache the inputs as well."
754 auto HighlightRangeCallback = [&](Rewriter &R, SourceLocation B,
755 SourceLocation E, const char *StartTag,
756 const char *EndTag, bool isTokenRange) {
757 HighlightRange(R, B, E, StartTag, EndTag, isTokenRange);
758
759 if (Cache) {
760 Cache->MacroHighlights[FID].push_back(
761 {B, E, StartTag, EndTag, isTokenRange});
762 }
763 };
764
765 HighlightMacrosImpl(R, FID, PP, HighlightRangeCallback);
766}
#define SM(sm)
Definition: Cuda.cpp:82
static void AddLineNumber(RewriteBuffer &RB, unsigned LineNo, unsigned B, unsigned E)
static void HighlightMacrosImpl(Rewriter &R, FileID FID, const Preprocessor &PP, llvm::function_ref< void(Rewriter &, SourceLocation, SourceLocation, const char *, const char *, bool)> HighlightRangeCallback)
static void SyntaxHighlightImpl(Rewriter &R, FileID FID, const Preprocessor &PP, llvm::function_ref< void(RewriteBuffer &, unsigned, unsigned, const char *, const char *, const char *)> HighlightRangeCallback)
SyntaxHighlight - Relex the specified FileID and annotate the HTML with information about keywords,...
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
__device__ __2f16 float __ockl_bool s
__device__ __2f16 float c
Represents a character-granular source range.
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
SourceLocation getEnd() const
SourceLocation getBegin() const
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:562
const IntrusiveRefCntPtr< DiagnosticIDs > & getDiagnosticIDs() const
Definition: Diagnostic.h:557
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
unsigned getHashValue() const
A diagnostic client that ignores all diagnostics.
Definition: Diagnostic.h:1800
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.
Definition: Lexer.h:78
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition: Lexer.h:236
void SetCommentRetentionState(bool Mode)
SetCommentRetentionMode - Change the comment retention mode of the lexer to the specified mode.
Definition: Lexer.h:269
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Definition: Lexer.cpp:499
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
IdentifierInfo * LookUpIdentifierInfo(Token &Identifier) const
Given a tok::raw_identifier token, look up the identifier information for the token and install it in...
void setDiagnostics(DiagnosticsEngine &D)
void Lex(Token &Result)
Lex the next token for this preprocessor.
SourceManager & getSourceManager() const
StringRef getSpelling(SourceLocation loc, SmallVectorImpl< char > &buffer, bool *invalid=nullptr) const
Return the 'spelling' of the token at the given location; does not go up to the spelling location or ...
const LangOptions & getLangOpts() const
void setPragmasEnabled(bool Enabled)
void SetCommentRetentionState(bool KeepComments, bool KeepMacroComments)
Control whether the preprocessor retains comments in output.
bool getPragmasEnabled() const
DiagnosticsEngine & getDiagnostics() const
RewriteBuffer - As code is rewritten, SourceBuffer's from the original input with modifications get a...
Definition: RewriteBuffer.h:25
void InsertTextAfter(unsigned OrigOffset, StringRef Str)
InsertTextAfter - Insert some text at the specified point, where the offset in the buffer is specifie...
Definition: RewriteBuffer.h:81
void InsertTextBefore(unsigned OrigOffset, StringRef Str)
InsertTextBefore - Insert some text before the specified point, where the offset in the buffer is spe...
Definition: RewriteBuffer.h:74
void ReplaceText(unsigned OrigOffset, unsigned OrigLength, StringRef NewStr)
ReplaceText - This method replaces a range of characters in the input buffer with a new string.
Definition: Rewriter.cpp:126
Rewriter - This is the main interface to the rewrite buffers.
Definition: Rewriter.h:32
bool InsertTextBefore(SourceLocation Loc, StringRef Str)
InsertText - Insert the specified string at the specified location in the original buffer.
Definition: Rewriter.h:136
SourceManager & getSourceMgr() const
Definition: Rewriter.h:77
const LangOptions & getLangOpts() const
Definition: Rewriter.h:78
bool InsertTextAfter(SourceLocation Loc, StringRef Str)
InsertTextAfter - Insert the specified string at the specified location in the original buffer.
Definition: Rewriter.h:123
RewriteBuffer & getEditBuffer(FileID FID)
getEditBuffer - This is like getRewriteBufferFor, but always returns a buffer, and allows you to writ...
Definition: Rewriter.cpp:237
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
llvm::MemoryBufferRef getBufferOrFake(FileID FID, SourceLocation Loc=SourceLocation()) const
Return the buffer for the specified FileID.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
TokenConcatenation class, which answers the question of "Is it safe to emit two tokens without a whit...
Token - This structure provides full information about a lexed token.
Definition: Token.h:36
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:132
unsigned getLength() const
Definition: Token.h:135
void setKind(tok::TokenKind K)
Definition: Token.h:95
bool is(tok::TokenKind K) const
is/isNot - Predicates to check if this token is a specific kind, as in "if (Tok.is(tok::l_brace)) {....
Definition: Token.h:99
tok::TokenKind getKind() const
Definition: Token.h:94
bool isAtStartOfLine() const
isAtStartOfLine - Return true if this token is at the start of a line.
Definition: Token.h:276
bool hasLeadingSpace() const
Return true if this token has whitespace before it.
Definition: Token.h:280
bool isNot(tok::TokenKind K) const
Definition: Token.h:100
The type-property cache.
Definition: Type.cpp:4261
void AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID, StringRef title)
void HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E, const char *StartTag, const char *EndTag, bool IsTokenRange=true)
HighlightRange - Highlight a range in the source code with the specified start/end tags.
Definition: HTMLRewrite.cpp:33
RelexRewriteCacheRef instantiateRelexRewriteCache()
If you need to rewrite the same file multiple times, you can instantiate a RelexRewriteCache and refe...
void AddLineNumbers(Rewriter &R, FileID FID)
void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP, RelexRewriteCacheRef Cache=nullptr)
SyntaxHighlight - Relex the specified FileID and annotate the HTML with information about keywords,...
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP, RelexRewriteCacheRef Cache=nullptr)
HighlightMacros - This uses the macro table state from the end of the file, to reexpand macros and in...
void EscapeText(Rewriter &R, FileID FID, bool EscapeSpaces=false, bool ReplaceTabs=false)
EscapeText - HTMLize a specified file so that special characters are are translated so that they are ...
std::shared_ptr< RelexRewriteCache > RelexRewriteCacheRef
Definition: HTMLRewrite.h:28
The JSON file list parser is used to communicate input to InstallAPI.
YAML serialization mapping.
Definition: Dominators.h:30
std::vector< RawHighlight > RawHighlightList
DenseMap< FileID, RawHighlightList > SyntaxHighlights
std::vector< Highlight > HighlightList
DenseMap< FileID, HighlightList > MacroHighlights