Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions lib/rdoc/cross_reference.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require_relative 'markup/attribute_manager' # for PROTECT_ATTR

##
# RDoc::CrossReference is a reusable way to create cross references for names.

Expand Down Expand Up @@ -33,7 +31,7 @@ class RDoc::CrossReference
# See CLASS_REGEXP_STR

METHOD_REGEXP_STR = /(
(?!\d)[\w#{RDoc::Markup::AttributeManager::PROTECT_ATTR}]+[!?=]?|
(?!\d)[\w]+[!?=]?|
%|=(?:==?|~)|![=~]|\[\]=?|<(?:<|=>?)?|>[>=]?|[-+!]@?|\*\*?|[\/%\`|&^~]
)#{METHOD_ARGS_REGEXP_STR}/.source.delete("\n ").freeze

Expand Down
5 changes: 5 additions & 0 deletions lib/rdoc/generator/template/aliki/_head.rhtml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@
defer
></script>

<script
src="<%= h asset_rel_prefix %>/js/bash_highlighter.js?v=<%= h RDoc::VERSION %>"
defer
></script>

<script
src="<%= h asset_rel_prefix %>/js/aliki.js?v=<%= h RDoc::VERSION %>"
defer
Expand Down
64 changes: 28 additions & 36 deletions lib/rdoc/generator/template/aliki/css/rdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,14 @@
--color-neutral-800: #292524;
--color-neutral-900: #1c1917;

/* Code highlighting colors */
/* Code highlighting colors - neutral palette for all syntax highlighters */
--code-blue: #1d4ed8;
--code-green: #047857;
--code-orange: #d97706;
--code-purple: #7e22ce;
--code-red: #dc2626;

/* C syntax highlighting */
--c-keyword: #b91c1c;
--c-type: #0891b2;
--c-macro: #ea580c;
--c-function: #7c3aed;
--c-identifier: #475569;
--c-operator: #059669;
--c-preprocessor: #a21caf;
--c-value: #92400e;
--c-string: #15803d;
--c-comment: #78716c;
--code-cyan: #0891b2;
--code-gray: #78716c;

/* Color Palette - Green (for success states) */
--color-green-400: #4ade80;
Expand Down Expand Up @@ -186,24 +176,14 @@

/* Dark Theme */
[data-theme="dark"] {
/* Code highlighting colors */
/* Code highlighting colors - neutral palette for all syntax highlighters */
--code-blue: #93c5fd;
--code-green: #34d399;
--code-orange: #fbbf24;
--code-purple: #c084fc;
--code-red: #f87171;

/* C syntax highlighting */
--c-keyword: #f87171;
--c-type: #22d3ee;
--c-macro: #fb923c;
--c-function: #a78bfa;
--c-identifier: #94a3b8;
--c-operator: #6ee7b7;
--c-preprocessor: #e879f9;
--c-value: #fcd34d;
--c-string: #4ade80;
--c-comment: #a8a29e;
--code-cyan: #22d3ee;
--code-gray: #a8a29e;

/* Semantic Colors - Dark Theme */
--color-text-primary: var(--color-neutral-50);
Expand Down Expand Up @@ -1064,18 +1044,30 @@ main h6 a:hover {
[data-theme="dark"] .ruby-string { color: var(--code-green); }

/* C Syntax Highlighting */
.c-keyword { color: var(--c-keyword); }
.c-type { color: var(--c-type); }
.c-macro { color: var(--c-macro); }
.c-function { color: var(--c-function); }
.c-identifier { color: var(--c-identifier); }
.c-operator { color: var(--c-operator); }
.c-preprocessor { color: var(--c-preprocessor); }
.c-value { color: var(--c-value); }
.c-string { color: var(--c-string); }
.c-keyword { color: var(--code-red); }
.c-type { color: var(--code-cyan); }
.c-macro { color: var(--code-orange); }
.c-function { color: var(--code-purple); }
.c-identifier { color: var(--color-text-secondary); }
.c-operator { color: var(--code-green); }
.c-preprocessor { color: var(--code-purple); }
.c-value { color: var(--code-orange); }
.c-string { color: var(--code-green); }

.c-comment {
color: var(--c-comment);
color: var(--code-gray);
font-style: italic;
}

/* Shell Syntax Highlighting */
.sh-prompt { color: var(--code-gray); }
.sh-command { color: var(--code-blue); }
.sh-option { color: var(--code-cyan); }
.sh-string { color: var(--code-green); }
.sh-envvar { color: var(--code-purple); }

.sh-comment {
color: var(--code-gray);
font-style: italic;
}

Expand Down
167 changes: 167 additions & 0 deletions lib/rdoc/generator/template/aliki/js/bash_highlighter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* Client-side shell syntax highlighter for RDoc
* Highlights: $ prompts, commands, options, strings, env vars, comments
*/

(function() {
'use strict';

function escapeHtml(text) {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}

function wrap(className, text) {
return '<span class="' + className + '">' + escapeHtml(text) + '</span>';
}

function highlightLine(line) {
if (line.trim() === '') return escapeHtml(line);

var result = '';
var i = 0;
var len = line.length;

// Preserve leading whitespace
while (i < len && (line[i] === ' ' || line[i] === '\t')) {
result += escapeHtml(line[i++]);
}

// Check for $ prompt ($ followed by space or end of line)
if (line[i] === '$' && (line[i + 1] === ' ' || line[i + 1] === undefined)) {
result += wrap('sh-prompt', '$');
i++;
}

// Check for # comment at start
if (line[i] === '#') {
return result + wrap('sh-comment', line.slice(i));
}

var seenCommand = false;
var afterSpace = true;

while (i < len) {
var ch = line[i];

// Whitespace
if (ch === ' ' || ch === '\t') {
result += escapeHtml(ch);
i++;
afterSpace = true;
continue;
}

// Comment after whitespace
if (ch === '#' && afterSpace) {
result += wrap('sh-comment', line.slice(i));
break;
}

// Double-quoted string
if (ch === '"') {
var end = i + 1;
while (end < len && line[end] !== '"') {
if (line[end] === '\\' && end + 1 < len) end += 2;
else end++;
}
if (end < len) end++;
result += wrap('sh-string', line.slice(i, end));
i = end;
afterSpace = false;
continue;
}

// Single-quoted string
if (ch === "'") {
var end = i + 1;
while (end < len && line[end] !== "'") end++;
if (end < len) end++;
result += wrap('sh-string', line.slice(i, end));
i = end;
afterSpace = false;
continue;
}

// Environment variable (ALLCAPS=)
if (afterSpace && /[A-Z]/.test(ch)) {
var match = line.slice(i).match(/^[A-Z][A-Z0-9_]*=/);
if (match) {
result += wrap('sh-envvar', match[0]);
i += match[0].length;
// Read unquoted value
var valEnd = i;
while (valEnd < len && line[valEnd] !== ' ' && line[valEnd] !== '\t' && line[valEnd] !== '"' && line[valEnd] !== "'") valEnd++;
if (valEnd > i) {
result += escapeHtml(line.slice(i, valEnd));
i = valEnd;
}
afterSpace = false;
continue;
}
}

// Option (must be after whitespace)
if (ch === '-' && afterSpace) {
var match = line.slice(i).match(/^--?[a-zA-Z0-9_-]+(=[^"'\s]*)?/);
if (match) {
result += wrap('sh-option', match[0]);
i += match[0].length;
afterSpace = false;
continue;
}
}

// Command (first word: regular, ./path, ../path, ~/path, /abs/path, @scope/pkg)
if (!seenCommand && afterSpace) {
var isCmd = /[a-zA-Z0-9@~\/]/.test(ch) ||
(ch === '.' && (line[i + 1] === '/' || (line[i + 1] === '.' && line[i + 2] === '/')));
if (isCmd) {
var end = i;
while (end < len && line[end] !== ' ' && line[end] !== '\t') end++;
result += wrap('sh-command', line.slice(i, end));
i = end;
seenCommand = true;
afterSpace = false;
continue;
}
}

// Everything else
result += escapeHtml(ch);
i++;
afterSpace = false;
}

return result;
}

function highlightShell(code) {
return code.split('\n').map(highlightLine).join('\n');
}

function initHighlighting() {
var selectors = [
'pre.bash', 'pre.sh', 'pre.shell', 'pre.console',
'pre[data-language="bash"]', 'pre[data-language="sh"]',
'pre[data-language="shell"]', 'pre[data-language="console"]'
];

var blocks = document.querySelectorAll(selectors.join(', '));
blocks.forEach(function(block) {
if (block.getAttribute('data-highlighted') === 'true') return;
block.innerHTML = highlightShell(block.textContent);
block.setAttribute('data-highlighted', 'true');
});
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initHighlighting);
} else {
initHighlighting();
}
})();
38 changes: 8 additions & 30 deletions lib/rdoc/markup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
#
# class WikiHtml < RDoc::Markup::ToHtml
# def handle_regexp_WIKIWORD(target)
# "<font color=red>" + target.text + "</font>"
# "<font color=red>" + target + "</font>"
# end
# end
#
Expand Down Expand Up @@ -110,10 +110,10 @@

class RDoc::Markup

##
# An AttributeManager which handles inline markup.
# Array of regexp handling pattern and its name. A regexp handling
# sequence is something like a WikiWord

attr_reader :attribute_manager
attr_reader :regexp_handlings

##
# Parses +str+ into an RDoc::Markup::Document.
Expand Down Expand Up @@ -148,27 +148,11 @@ def self.parse(str)
# structure (paragraphs, lists, and so on). Invoke an event handler as we
# identify significant chunks.

def initialize(attribute_manager = nil)
@attribute_manager = attribute_manager || RDoc::Markup::AttributeManager.new
def initialize
@regexp_handlings = []
@output = nil
end

##
# Add to the sequences used to add formatting to an individual word (such
# as *bold*). Matching entries will generate attributes that the output
# formatters can recognize by their +name+.

def add_word_pair(start, stop, name)
@attribute_manager.add_word_pair(start, stop, name)
end

##
# Add to the sequences recognized as general markup.

def add_html(tag, name)
@attribute_manager.add_html(tag, name)
end

##
# Add to other inline sequences. For example, we could add WikiWords using
# something like:
Expand All @@ -178,7 +162,7 @@ def add_html(tag, name)
# Each wiki word will be presented to the output formatter.

def add_regexp_handling(pattern, name)
@attribute_manager.add_regexp_handling(pattern, name)
@regexp_handlings << [pattern, name]
end

##
Expand All @@ -197,15 +181,9 @@ def convert(input, formatter)
end

autoload :Parser, "#{__dir__}/markup/parser"
autoload :InlineParser, "#{__dir__}/markup/inline_parser"
autoload :PreProcess, "#{__dir__}/markup/pre_process"

# Inline markup classes
autoload :AttrChanger, "#{__dir__}/markup/attr_changer"
autoload :AttrSpan, "#{__dir__}/markup/attr_span"
autoload :Attributes, "#{__dir__}/markup/attributes"
autoload :AttributeManager, "#{__dir__}/markup/attribute_manager"
autoload :RegexpHandling, "#{__dir__}/markup/regexp_handling"

# RDoc::Markup AST
autoload :BlankLine, "#{__dir__}/markup/blank_line"
autoload :BlockQuote, "#{__dir__}/markup/block_quote"
Expand Down
22 changes: 0 additions & 22 deletions lib/rdoc/markup/attr_changer.rb

This file was deleted.

Loading
Loading