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