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