mirror of
https://github.com/boostorg/website-v2-docs.git
synced 2026-01-19 04:42:17 +00:00
feat: add custom C++ syntax highlighting support (#556)
This commit is contained in:
36
antora-ui/src/css/cpp-highlight.css
Normal file
36
antora-ui/src/css/cpp-highlight.css
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* cpp-highlight.css - Custom C++ syntax highlighting styles
|
||||
* This replaces highlight.js's C++ highlighting for consistent styling
|
||||
*/
|
||||
|
||||
/* C++ Keywords - blue, bold */
|
||||
code.cpp-highlight .cpp-keyword,
|
||||
.doc pre.highlight code.cpp-highlight .cpp-keyword {
|
||||
color: #00f;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* C++ Strings - dark red */
|
||||
code.cpp-highlight .cpp-string,
|
||||
.doc pre.highlight code.cpp-highlight .cpp-string {
|
||||
color: #a31515;
|
||||
}
|
||||
|
||||
/* C++ Preprocessor directives - purple */
|
||||
code.cpp-highlight .cpp-preprocessor,
|
||||
.doc pre.highlight code.cpp-highlight .cpp-preprocessor {
|
||||
color: #6f008a;
|
||||
}
|
||||
|
||||
/* C++ Comments - green, italic */
|
||||
code.cpp-highlight .cpp-comment,
|
||||
.doc pre.highlight code.cpp-highlight .cpp-comment {
|
||||
color: #008000;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Base text in C++ blocks - plain black (identifiers, function names, etc.) */
|
||||
code.cpp-highlight,
|
||||
.doc pre.highlight code.cpp-highlight {
|
||||
color: inherit;
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
@import "header.css";
|
||||
@import "footer.css";
|
||||
@import "highlight.css";
|
||||
@import "cpp-highlight.css";
|
||||
@import "print.css";
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
|
||||
199
antora-ui/src/js/vendor/cpp-highlight.js
vendored
Normal file
199
antora-ui/src/js/vendor/cpp-highlight.js
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* cpp-highlight.js - Lightweight C++ syntax highlighter
|
||||
*
|
||||
* Categories:
|
||||
* - keyword : Language keywords (const, while, if, etc.)
|
||||
* - string : String and character literals
|
||||
* - preprocessor: Preprocessor directives (#include, #define, etc.)
|
||||
* - comment : C and C++ style comments
|
||||
*/
|
||||
|
||||
const CppHighlight = (function () {
|
||||
'use strict'
|
||||
|
||||
const KEYWORDS = new Set([
|
||||
// Storage class
|
||||
'auto', 'register', 'static', 'extern', 'mutable', 'thread_local',
|
||||
// Type qualifiers
|
||||
'const', 'volatile', 'constexpr', 'consteval', 'constinit',
|
||||
// Type specifiers
|
||||
'void', 'bool', 'char', 'short', 'int', 'long', 'float', 'double',
|
||||
'signed', 'unsigned', 'wchar_t', 'char8_t', 'char16_t', 'char32_t',
|
||||
// Complex types
|
||||
'class', 'struct', 'union', 'enum', 'typename', 'typedef',
|
||||
// Control flow
|
||||
'if', 'else', 'switch', 'case', 'default', 'for', 'while', 'do',
|
||||
'break', 'continue', 'return', 'goto',
|
||||
// Exception handling
|
||||
'try', 'catch', 'throw', 'noexcept',
|
||||
// OOP
|
||||
'public', 'private', 'protected', 'virtual', 'override', 'final',
|
||||
'friend', 'this', 'operator', 'new', 'delete',
|
||||
// Templates
|
||||
'template', 'concept', 'requires',
|
||||
// Namespace
|
||||
'namespace', 'using',
|
||||
// Other
|
||||
'sizeof', 'alignof', 'alignas', 'decltype', 'typeid',
|
||||
'static_cast', 'dynamic_cast', 'const_cast', 'reinterpret_cast',
|
||||
'static_assert', 'inline', 'explicit', 'export', 'module', 'import',
|
||||
'co_await', 'co_yield', 'co_return',
|
||||
// Literals
|
||||
'true', 'false', 'nullptr', 'NULL',
|
||||
])
|
||||
|
||||
function escapeHtml (text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
}
|
||||
|
||||
function span (cls, content) {
|
||||
return `<span class="cpp-${cls}">${escapeHtml(content)}</span>`
|
||||
}
|
||||
|
||||
function highlight (code) {
|
||||
const result = []
|
||||
let i = 0
|
||||
const n = code.length
|
||||
|
||||
while (i < n) {
|
||||
// Line comment
|
||||
if (code[i] === '/' && code[i + 1] === '/') {
|
||||
let end = i + 2
|
||||
while (end < n && code[end] !== '\n') end++
|
||||
result.push(span('comment', code.slice(i, end)))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Block comment
|
||||
if (code[i] === '/' && code[i + 1] === '*') {
|
||||
let end = i + 2
|
||||
while (end < n - 1 && !(code[end] === '*' && code[end + 1] === '/')) end++
|
||||
end += 2
|
||||
result.push(span('comment', code.slice(i, end)))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Preprocessor directive (at start of line or after whitespace)
|
||||
if (code[i] === '#') {
|
||||
// Check if # is at line start (allow leading whitespace)
|
||||
let checkPos = i - 1
|
||||
while (checkPos >= 0 && (code[checkPos] === ' ' || code[checkPos] === '\t')) checkPos--
|
||||
if (checkPos < 0 || code[checkPos] === '\n') {
|
||||
let end = i + 1
|
||||
// Handle line continuation with backslash
|
||||
while (end < n) {
|
||||
if (code[end] === '\n') {
|
||||
if (code[end - 1] === '\\') {
|
||||
end++
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
end++
|
||||
}
|
||||
result.push(span('preprocessor', code.slice(i, end)))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Raw string literal R"delimiter(...)delimiter"
|
||||
if (code[i] === 'R' && code[i + 1] === '"') {
|
||||
let delimEnd = i + 2
|
||||
while (delimEnd < n && code[delimEnd] !== '(') delimEnd++
|
||||
const delimiter = code.slice(i + 2, delimEnd)
|
||||
const endMarker = ')' + delimiter + '"'
|
||||
let end = delimEnd + 1
|
||||
while (end < n) {
|
||||
if (code.slice(end, end + endMarker.length) === endMarker) {
|
||||
end += endMarker.length
|
||||
break
|
||||
}
|
||||
end++
|
||||
}
|
||||
result.push(span('string', code.slice(i, end)))
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// String literal (with optional prefix)
|
||||
if (code[i] === '"' ||
|
||||
((code[i] === 'L' || code[i] === 'u' || code[i] === 'U') && code[i + 1] === '"') ||
|
||||
(code[i] === 'u' && code[i + 1] === '8' && code[i + 2] === '"')) {
|
||||
const start = i
|
||||
if (code[i] === 'u' && code[i + 1] === '8') i += 2
|
||||
else if (code[i] !== '"') i++
|
||||
i++ // skip opening quote
|
||||
while (i < n && code[i] !== '"') {
|
||||
if (code[i] === '\\' && i + 1 < n) i += 2
|
||||
else i++
|
||||
}
|
||||
i++ // skip closing quote
|
||||
result.push(span('string', code.slice(start, i)))
|
||||
continue
|
||||
}
|
||||
|
||||
// Character literal
|
||||
if (code[i] === '\'' ||
|
||||
((code[i] === 'L' || code[i] === 'u' || code[i] === 'U') && code[i + 1] === '\'') ||
|
||||
(code[i] === 'u' && code[i + 1] === '8' && code[i + 2] === '\'')) {
|
||||
const start = i
|
||||
if (code[i] === 'u' && code[i + 1] === '8') i += 2
|
||||
else if (code[i] !== '\'') i++
|
||||
i++ // skip opening quote
|
||||
while (i < n && code[i] !== '\'') {
|
||||
if (code[i] === '\\' && i + 1 < n) i += 2
|
||||
else i++
|
||||
}
|
||||
i++ // skip closing quote
|
||||
result.push(span('string', code.slice(start, i)))
|
||||
continue
|
||||
}
|
||||
|
||||
// Identifier or keyword
|
||||
if (/[a-zA-Z_]/.test(code[i])) {
|
||||
let end = i + 1
|
||||
while (end < n && /[a-zA-Z0-9_]/.test(code[end])) end++
|
||||
const word = code.slice(i, end)
|
||||
if (KEYWORDS.has(word)) {
|
||||
result.push(span('keyword', word))
|
||||
} else {
|
||||
result.push(escapeHtml(word))
|
||||
}
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// Default: single character
|
||||
result.push(escapeHtml(code[i]))
|
||||
i++
|
||||
}
|
||||
|
||||
return result.join('')
|
||||
}
|
||||
|
||||
function highlightElement (el) {
|
||||
el.innerHTML = highlight(el.textContent)
|
||||
}
|
||||
|
||||
function highlightAll (selector = 'code.cpp, code.c++, pre.cpp, pre.c++') {
|
||||
document.querySelectorAll(selector).forEach(highlightElement)
|
||||
}
|
||||
|
||||
return {
|
||||
highlight,
|
||||
highlightElement,
|
||||
highlightAll,
|
||||
KEYWORDS,
|
||||
}
|
||||
})()
|
||||
|
||||
// CommonJS / ES module support
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = CppHighlight
|
||||
}
|
||||
83
antora-ui/src/js/vendor/highlight.bundle.js
vendored
83
antora-ui/src/js/vendor/highlight.bundle.js
vendored
@@ -2,11 +2,92 @@
|
||||
'use strict'
|
||||
|
||||
var hljs = require('highlight.js/lib/common')
|
||||
var CppHighlight = require('./cpp-highlight')
|
||||
|
||||
// Only register languages not included in common bundle
|
||||
hljs.registerLanguage('cmake', require('highlight.js/lib/languages/cmake'))
|
||||
|
||||
hljs.highlightAll()
|
||||
// Replace C++ highlighting AFTER highlight.js processes blocks
|
||||
// Let hljs work initially, then replace C++ blocks with custom highlighter
|
||||
function processCppBlocks () {
|
||||
// Selectors for C++ code blocks that highlight.js has already processed
|
||||
var cppSelectors = [
|
||||
'code.language-cpp.hljs',
|
||||
'code.language-c++.hljs',
|
||||
'code[data-lang="cpp"].hljs',
|
||||
'code[data-lang="c++"].hljs',
|
||||
'.doc pre.highlight code[data-lang="cpp"].hljs',
|
||||
'.doc pre.highlight code[data-lang="c++"].hljs',
|
||||
]
|
||||
|
||||
var processedCount = 0
|
||||
|
||||
cppSelectors.forEach(function (selector) {
|
||||
try {
|
||||
document.querySelectorAll(selector).forEach(function (el) {
|
||||
// Skip if already processed
|
||||
if (el.classList.contains('cpp-highlight')) return
|
||||
|
||||
// Replace highlight.js's C++ highlighting with our custom highlighter
|
||||
// This gives us full control over C++ syntax highlighting
|
||||
CppHighlight.highlightElement(el)
|
||||
|
||||
// Mark as processed with our custom highlighter
|
||||
el.classList.add('cpp-highlight')
|
||||
processedCount++
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn('cpp-highlight error:', selector, e)
|
||||
}
|
||||
})
|
||||
|
||||
if (processedCount > 0) {
|
||||
console.log('cpp-highlight: Replaced ' + processedCount + ' C++ code blocks')
|
||||
}
|
||||
}
|
||||
|
||||
// Process C++ blocks after highlight.js runs
|
||||
function initHighlighting () {
|
||||
// First, let highlight.js process everything
|
||||
hljs.highlightAll()
|
||||
|
||||
// Then, replace C++ blocks with our custom highlighter
|
||||
// Use setTimeout to ensure highlight.js is completely done
|
||||
setTimeout(function () {
|
||||
processCppBlocks()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// Process when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initHighlighting)
|
||||
} else {
|
||||
// DOM already loaded
|
||||
initHighlighting()
|
||||
}
|
||||
|
||||
// Also use MutationObserver to catch dynamically added content
|
||||
// Process C++ blocks after highlight.js processes new content
|
||||
if (typeof window.MutationObserver !== 'undefined') {
|
||||
var observer = new window.MutationObserver(function (mutations) {
|
||||
var shouldProcess = false
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.addedNodes.length > 0) {
|
||||
shouldProcess = true
|
||||
}
|
||||
})
|
||||
if (shouldProcess) {
|
||||
// Wait a bit for highlight.js to process new content
|
||||
setTimeout(function () {
|
||||
processCppBlocks()
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
observer.observe(document.body || document.documentElement, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
})
|
||||
}
|
||||
|
||||
window.hljs = hljs
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user