feat: add syntax highlighting for R, Ruby, PHP, and SQL
Cherry-picked from upstream 022c75da with conflict resolution. Merged our codeClassName support with upstream per-line highlighting.
This commit is contained in:
parent
b87082a915
commit
aeed5a0873
2 changed files with 224 additions and 4 deletions
|
|
@ -39,6 +39,7 @@ import {
|
|||
highlightSearchInChildren,
|
||||
type SearchContext,
|
||||
} from '../searchHighlightUtils';
|
||||
import { highlightLine } from '../viewers/syntaxHighlighter';
|
||||
|
||||
import { FileLink, isRelativeUrl } from './FileLink';
|
||||
import { MermaidDiagram } from './MermaidDiagram';
|
||||
|
|
@ -535,12 +536,21 @@ function createViewerMarkdownComponents(
|
|||
const isBlock = (hasLanguage ?? false) || isMultiLine;
|
||||
|
||||
if (isBlock) {
|
||||
const lang = codeClassName?.replace('language-', '') ?? '';
|
||||
const raw = typeof children === 'string' ? children : '';
|
||||
const text = raw.replace(/\n$/, '');
|
||||
const lines = text.split('\n');
|
||||
return (
|
||||
<code
|
||||
className={`font-mono text-xs ${codeClassName ?? ''}`.trim()}
|
||||
style={{ color: COLOR_TEXT }}
|
||||
>
|
||||
{hl(children)}
|
||||
{lines.map((line, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{hl(highlightLine(line, lang))}
|
||||
{i < lines.length - 1 ? '\n' : null}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,6 +208,200 @@ const KEYWORDS: Record<string, Set<string>> = {
|
|||
'true',
|
||||
'false',
|
||||
]),
|
||||
r: new Set([
|
||||
'if',
|
||||
'else',
|
||||
'for',
|
||||
'while',
|
||||
'repeat',
|
||||
'function',
|
||||
'return',
|
||||
'next',
|
||||
'break',
|
||||
'in',
|
||||
'library',
|
||||
'require',
|
||||
'source',
|
||||
'TRUE',
|
||||
'FALSE',
|
||||
'NULL',
|
||||
'NA',
|
||||
'Inf',
|
||||
'NaN',
|
||||
'NA_integer_',
|
||||
'NA_real_',
|
||||
'NA_complex_',
|
||||
'NA_character_',
|
||||
]),
|
||||
ruby: new Set([
|
||||
'def',
|
||||
'class',
|
||||
'module',
|
||||
'end',
|
||||
'do',
|
||||
'if',
|
||||
'elsif',
|
||||
'else',
|
||||
'unless',
|
||||
'while',
|
||||
'until',
|
||||
'for',
|
||||
'in',
|
||||
'begin',
|
||||
'rescue',
|
||||
'ensure',
|
||||
'raise',
|
||||
'return',
|
||||
'yield',
|
||||
'block_given?',
|
||||
'require',
|
||||
'require_relative',
|
||||
'include',
|
||||
'extend',
|
||||
'attr_accessor',
|
||||
'attr_reader',
|
||||
'attr_writer',
|
||||
'self',
|
||||
'super',
|
||||
'nil',
|
||||
'true',
|
||||
'false',
|
||||
'and',
|
||||
'or',
|
||||
'not',
|
||||
'then',
|
||||
'when',
|
||||
'case',
|
||||
'lambda',
|
||||
'proc',
|
||||
'puts',
|
||||
'print',
|
||||
]),
|
||||
php: new Set([
|
||||
'function',
|
||||
'class',
|
||||
'interface',
|
||||
'trait',
|
||||
'extends',
|
||||
'implements',
|
||||
'namespace',
|
||||
'use',
|
||||
'public',
|
||||
'private',
|
||||
'protected',
|
||||
'static',
|
||||
'abstract',
|
||||
'final',
|
||||
'const',
|
||||
'var',
|
||||
'new',
|
||||
'return',
|
||||
'if',
|
||||
'elseif',
|
||||
'else',
|
||||
'for',
|
||||
'foreach',
|
||||
'while',
|
||||
'do',
|
||||
'switch',
|
||||
'case',
|
||||
'break',
|
||||
'continue',
|
||||
'default',
|
||||
'try',
|
||||
'catch',
|
||||
'finally',
|
||||
'throw',
|
||||
'as',
|
||||
'echo',
|
||||
'print',
|
||||
'require',
|
||||
'require_once',
|
||||
'include',
|
||||
'include_once',
|
||||
'true',
|
||||
'false',
|
||||
'null',
|
||||
'array',
|
||||
'isset',
|
||||
'unset',
|
||||
'empty',
|
||||
'self',
|
||||
'this',
|
||||
]),
|
||||
sql: new Set([
|
||||
'SELECT',
|
||||
'FROM',
|
||||
'WHERE',
|
||||
'INSERT',
|
||||
'INTO',
|
||||
'UPDATE',
|
||||
'SET',
|
||||
'DELETE',
|
||||
'CREATE',
|
||||
'ALTER',
|
||||
'DROP',
|
||||
'TABLE',
|
||||
'INDEX',
|
||||
'VIEW',
|
||||
'DATABASE',
|
||||
'JOIN',
|
||||
'INNER',
|
||||
'LEFT',
|
||||
'RIGHT',
|
||||
'OUTER',
|
||||
'FULL',
|
||||
'CROSS',
|
||||
'ON',
|
||||
'AND',
|
||||
'OR',
|
||||
'NOT',
|
||||
'IN',
|
||||
'EXISTS',
|
||||
'BETWEEN',
|
||||
'LIKE',
|
||||
'IS',
|
||||
'NULL',
|
||||
'AS',
|
||||
'ORDER',
|
||||
'BY',
|
||||
'GROUP',
|
||||
'HAVING',
|
||||
'LIMIT',
|
||||
'OFFSET',
|
||||
'UNION',
|
||||
'ALL',
|
||||
'DISTINCT',
|
||||
'COUNT',
|
||||
'SUM',
|
||||
'AVG',
|
||||
'MIN',
|
||||
'MAX',
|
||||
'CASE',
|
||||
'WHEN',
|
||||
'THEN',
|
||||
'ELSE',
|
||||
'END',
|
||||
'BEGIN',
|
||||
'COMMIT',
|
||||
'ROLLBACK',
|
||||
'TRANSACTION',
|
||||
'PRIMARY',
|
||||
'KEY',
|
||||
'FOREIGN',
|
||||
'REFERENCES',
|
||||
'CONSTRAINT',
|
||||
'DEFAULT',
|
||||
'VALUES',
|
||||
'TRUE',
|
||||
'FALSE',
|
||||
'INTEGER',
|
||||
'VARCHAR',
|
||||
'TEXT',
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
'TIMESTAMP',
|
||||
]),
|
||||
};
|
||||
|
||||
// Extend tsx/jsx to use typescript/javascript keywords
|
||||
|
|
@ -296,8 +490,23 @@ export function highlightLine(line: string, language: string): React.ReactNode[]
|
|||
break;
|
||||
}
|
||||
|
||||
// Check for comment (# style for Python/Shell)
|
||||
if ((language === 'python' || language === 'bash') && remaining.startsWith('#')) {
|
||||
// Check for comment (# style for Python/Shell/R/Ruby/PHP)
|
||||
if (
|
||||
(language === 'python' || language === 'bash' || language === 'r' || language === 'ruby' || language === 'php') &&
|
||||
remaining.startsWith('#')
|
||||
) {
|
||||
segments.push(
|
||||
React.createElement(
|
||||
'span',
|
||||
{ key: currentPos, style: { color: 'var(--syntax-comment)', fontStyle: 'italic' } },
|
||||
remaining
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for comment (-- style for SQL)
|
||||
if (language === 'sql' && remaining.startsWith('--')) {
|
||||
segments.push(
|
||||
React.createElement(
|
||||
'span',
|
||||
|
|
@ -326,7 +535,8 @@ export function highlightLine(line: string, language: string): React.ReactNode[]
|
|||
const wordMatch = /^([a-zA-Z_$][a-zA-Z0-9_$]*)/.exec(remaining);
|
||||
if (wordMatch) {
|
||||
const word = wordMatch[1];
|
||||
if (keywords.has(word)) {
|
||||
// SQL keywords are case-insensitive
|
||||
if (keywords.has(word) || (language === 'sql' && keywords.has(word.toUpperCase()))) {
|
||||
segments.push(
|
||||
React.createElement(
|
||||
'span',
|
||||
|
|
|
|||
Loading…
Reference in a new issue