coderay 0.4.3.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +340 -0
- data/README +103 -0
- data/demo/demo_count.rb +10 -0
- data/demo/demo_css.rb +4 -0
- data/demo/demo_div.rb +19 -0
- data/demo/demo_dump.rb +15 -0
- data/demo/demo_encoder.rb +39 -0
- data/demo/demo_global_vars.rb +13 -0
- data/demo/demo_global_vars2.rb +28 -0
- data/demo/demo_html.rb +394 -0
- data/demo/demo_html2.rb +11 -0
- data/demo/demo_load_encoder.rb +17 -0
- data/demo/demo_more.rb +204 -0
- data/demo/demo_scanner.rb +36 -0
- data/demo/demo_server.rb +92 -0
- data/demo/demo_simple.rb +10 -0
- data/demo/demo_stream.rb +25 -0
- data/demo/demo_stream2.rb +8 -0
- data/demo/demo_tokens.rb +3 -0
- data/lib/coderay.rb +284 -0
- data/lib/coderay/encoder.rb +151 -0
- data/lib/coderay/encoders/count.rb +21 -0
- data/lib/coderay/encoders/div.rb +16 -0
- data/lib/coderay/encoders/helpers/html_css.rb +155 -0
- data/lib/coderay/encoders/helpers/html_helper.rb +68 -0
- data/lib/coderay/encoders/helpers/html_output.rb +237 -0
- data/lib/coderay/encoders/html.rb +169 -0
- data/lib/coderay/encoders/null.rb +20 -0
- data/lib/coderay/encoders/span.rb +16 -0
- data/lib/coderay/encoders/statistic.rb +74 -0
- data/lib/coderay/encoders/text.rb +33 -0
- data/lib/coderay/encoders/tokens.rb +44 -0
- data/lib/coderay/encoders/yaml.rb +19 -0
- data/lib/coderay/helpers/filetype.rb +145 -0
- data/lib/coderay/helpers/gzip_simple.rb +123 -0
- data/lib/coderay/helpers/plugin.rb +286 -0
- data/lib/coderay/helpers/scanner_helper.rb +63 -0
- data/lib/coderay/scanner.rb +197 -0
- data/lib/coderay/scanners/c.rb +147 -0
- data/lib/coderay/scanners/delphi.rb +123 -0
- data/lib/coderay/scanners/helpers/ruby_helper.rb +212 -0
- data/lib/coderay/scanners/plaintext.rb +13 -0
- data/lib/coderay/scanners/ruby.rb +337 -0
- data/lib/coderay/tokens.rb +324 -0
- metadata +89 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
module CodeRay
|
2
|
+
|
3
|
+
# This module holds the Encoder class and its subclasses.
|
4
|
+
# For example, the HTML encoder is named CodeRay::Encoders::HTML
|
5
|
+
# can be found in coderay/encoders/html.
|
6
|
+
#
|
7
|
+
# Encoders also provides methods and constants for the register
|
8
|
+
# mechanism and the [] method that returns the Encoder class
|
9
|
+
# belonging to the given format.
|
10
|
+
module Encoders
|
11
|
+
extend PluginHost
|
12
|
+
plugin_path 'coderay/encoders'
|
13
|
+
|
14
|
+
# = Encoder
|
15
|
+
#
|
16
|
+
# The Encoder base class. Together with Scanner and
|
17
|
+
# Tokens, it forms the highlighting triad.
|
18
|
+
#
|
19
|
+
# Encoder instances take a Tokens object and do something with it.
|
20
|
+
#
|
21
|
+
# The most common Encoder is surely the HTML encoder
|
22
|
+
# (CodeRay::Encoders::HTML). It highlights the code in a colorful
|
23
|
+
# html page.
|
24
|
+
# If you want the highlighted code in a div or a span instead,
|
25
|
+
# use its subclasses Div and Span.
|
26
|
+
class Encoder
|
27
|
+
extend Plugin
|
28
|
+
plugin_host Encoders
|
29
|
+
|
30
|
+
attr_reader :token_stream
|
31
|
+
|
32
|
+
class << self
|
33
|
+
|
34
|
+
# Returns if the Encoder can be used in streaming mode.
|
35
|
+
def streamable?
|
36
|
+
is_a? Streamable
|
37
|
+
end
|
38
|
+
|
39
|
+
# If FILE_EXTENSION isn't defined, this method returns the
|
40
|
+
# downcase class name instead.
|
41
|
+
def const_missing sym
|
42
|
+
if sym == :FILE_EXTENSION
|
43
|
+
sym.to_s.downcase
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# Subclasses are to store their default options in this constant.
|
52
|
+
DEFAULT_OPTIONS = { :stream => false }
|
53
|
+
|
54
|
+
# The options you gave the Encoder at creating.
|
55
|
+
attr_accessor :options
|
56
|
+
|
57
|
+
# Creates a new Encoder.
|
58
|
+
# +options+ is saved and used for all encode operations, as long
|
59
|
+
# as you don't overwrite it there by passing additional options.
|
60
|
+
#
|
61
|
+
# Encoder objects provide three encode methods:
|
62
|
+
# - encode simply takes a +code+ string and a +lang+
|
63
|
+
# - encode_tokens expects a +tokens+ object instead
|
64
|
+
# - encode_stream is like encode, but uses streaming mode.
|
65
|
+
#
|
66
|
+
# Each method has an optional +options+ parameter. These are
|
67
|
+
# added to the options you passed at creation.
|
68
|
+
def initialize options = {}
|
69
|
+
@options = self.class::DEFAULT_OPTIONS.merge options
|
70
|
+
raise "I am only the basic Encoder class. I can't encode "\
|
71
|
+
"anything. :( Use my subclasses." if self.class == Encoder
|
72
|
+
end
|
73
|
+
|
74
|
+
# Encode a Tokens object.
|
75
|
+
def encode_tokens tokens, options = {}
|
76
|
+
options = @options.merge options
|
77
|
+
setup options
|
78
|
+
compile tokens, options
|
79
|
+
finish options
|
80
|
+
end
|
81
|
+
|
82
|
+
# Encode the given +code+ after tokenizing it using the Scanner
|
83
|
+
# for +lang+.
|
84
|
+
def encode code, lang, options = {}
|
85
|
+
options = @options.merge options
|
86
|
+
scanner_options = CodeRay.get_scanner_options(options)
|
87
|
+
tokens = CodeRay.scan code, lang, scanner_options
|
88
|
+
encode_tokens tokens, options
|
89
|
+
end
|
90
|
+
|
91
|
+
# You can use highlight instead of encode, if that seems
|
92
|
+
# more clear to you.
|
93
|
+
alias highlight encode
|
94
|
+
|
95
|
+
# Encode the given +code+ using the Scanner for +lang+ in
|
96
|
+
# streaming mode.
|
97
|
+
def encode_stream code, lang, options = {}
|
98
|
+
raise NotStreamableError, self unless kind_of? Streamable
|
99
|
+
options = @options.merge options
|
100
|
+
setup options
|
101
|
+
scanner_options = CodeRay.get_scanner_options options
|
102
|
+
@token_stream =
|
103
|
+
CodeRay.scan_stream code, lang, scanner_options, &self
|
104
|
+
finish options
|
105
|
+
end
|
106
|
+
|
107
|
+
# Behave like a proc. The token method is converted to a proc.
|
108
|
+
def to_proc
|
109
|
+
method(:token).to_proc
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
# Called with merged options before encoding starts.
|
115
|
+
# Sets @out to an empty string.
|
116
|
+
#
|
117
|
+
# See the HTML Encoder for an example of option caching.
|
118
|
+
def setup options
|
119
|
+
@out = ''
|
120
|
+
end
|
121
|
+
|
122
|
+
# Called with +text+ and +kind+ of the currently scanned token.
|
123
|
+
# For simple scanners, it's enougth to implement this method.
|
124
|
+
#
|
125
|
+
# Raises a NotImplementedError exception if it is not overwritten
|
126
|
+
# in subclass.
|
127
|
+
def token text, kind
|
128
|
+
raise NotImplementedError,
|
129
|
+
"#{self.class}#token not implemented."
|
130
|
+
end
|
131
|
+
|
132
|
+
# Called with merged options after encoding starts.
|
133
|
+
# The return value is the result of encoding, typically @out.
|
134
|
+
def finish options
|
135
|
+
@out
|
136
|
+
end
|
137
|
+
|
138
|
+
# Do the encoding.
|
139
|
+
#
|
140
|
+
# The already created +tokens+ object must be used; it can be a
|
141
|
+
# TokenStream or a Tokens object.
|
142
|
+
def compile tokens, options
|
143
|
+
tokens.each(&self)
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# vim:sw=2:ts=2:noet:tw=78
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CodeRay module Encoders
|
2
|
+
|
3
|
+
require 'coderay/encoders/html'
|
4
|
+
class Div < HTML
|
5
|
+
|
6
|
+
FILE_EXTENSION = 'div.html'
|
7
|
+
|
8
|
+
register_for :div
|
9
|
+
|
10
|
+
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge({
|
11
|
+
:css => :style,
|
12
|
+
:wrap => :div,
|
13
|
+
})
|
14
|
+
end
|
15
|
+
|
16
|
+
end end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module CodeRay module Encoders
|
2
|
+
|
3
|
+
class HTML
|
4
|
+
class CSS
|
5
|
+
|
6
|
+
def initialize stylesheet = TOKENS
|
7
|
+
@classes = Hash.new
|
8
|
+
parse stylesheet
|
9
|
+
end
|
10
|
+
|
11
|
+
def [] *styles
|
12
|
+
cl = @classes[styles.first]
|
13
|
+
return '' unless cl
|
14
|
+
style = false
|
15
|
+
1.upto(cl.size + 1) do |offset|
|
16
|
+
break if style = cl[styles[offset .. -1]]
|
17
|
+
end
|
18
|
+
return style
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
CSS_CLASS_PATTERN = /
|
24
|
+
( (?: # $1 = classes
|
25
|
+
\s* \. [-\w]+
|
26
|
+
)+ )
|
27
|
+
\s* \{
|
28
|
+
( [^\}]* ) # $2 = style
|
29
|
+
\} \s*
|
30
|
+
|
|
31
|
+
( . ) # $3 = error
|
32
|
+
/mx
|
33
|
+
def parse stylesheet
|
34
|
+
stylesheet.scan CSS_CLASS_PATTERN do |classes, style, error|
|
35
|
+
raise "CSS parse error: '#{error.inspect}' not recognized" if error
|
36
|
+
styles = classes.scan(/[-\w]+/)
|
37
|
+
cl = styles.pop
|
38
|
+
@classes[cl] ||= Hash.new
|
39
|
+
@classes[cl][styles] = style.strip
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
MAIN = <<-'MAIN'
|
44
|
+
.CodeRay {
|
45
|
+
background-color: #f8f8f8;
|
46
|
+
border: 1px solid silver;
|
47
|
+
font-family: 'Courier New', 'Terminal', monospace;
|
48
|
+
color: black;
|
49
|
+
width: 100%;
|
50
|
+
padding: 2px;
|
51
|
+
}
|
52
|
+
.CodeRay pre { margin: 0px; }
|
53
|
+
|
54
|
+
div.CodeRay { }
|
55
|
+
|
56
|
+
span.CodeRay { white-space: pre; border: 0; }
|
57
|
+
|
58
|
+
table.CodeRay { border-collapse: collapse; }
|
59
|
+
table.CodeRay td { padding: 2px 4px; vertical-align: top; }
|
60
|
+
|
61
|
+
.CodeRay .line_numbers, .CodeRay .no {
|
62
|
+
background-color: #def;
|
63
|
+
color: gray;
|
64
|
+
text-align: right;
|
65
|
+
}
|
66
|
+
.CodeRay .line_numbers tt { font-weight: bold; }
|
67
|
+
.CodeRay .no { padding: 0px 4px; }
|
68
|
+
.CodeRay .code { width: 100%; }
|
69
|
+
|
70
|
+
.CodeRay .code {
|
71
|
+
}
|
72
|
+
.CodeRay .code pre { overflow: auto; }
|
73
|
+
MAIN
|
74
|
+
|
75
|
+
TOKENS = <<-'TOKENS'
|
76
|
+
.af { color:#00C; }
|
77
|
+
.an { color:#007; }
|
78
|
+
.av { color:#700; }
|
79
|
+
.aw { color:#C00; }
|
80
|
+
.bi { color:#509; font-weight:bold; }
|
81
|
+
.c { color:#888; }
|
82
|
+
|
83
|
+
.ch { color:#04D; }
|
84
|
+
.ch .k { color:#04D; }
|
85
|
+
.ch .dl { color:#039; }
|
86
|
+
|
87
|
+
.cl { color:#B06; font-weight:bold; }
|
88
|
+
.co { color:#036; font-weight:bold; }
|
89
|
+
.cr { color:#0A0; }
|
90
|
+
.cv { color:#369; }
|
91
|
+
.df { color:#099; font-weight:bold; }
|
92
|
+
.di { color:#088; font-weight:bold; }
|
93
|
+
.dl { color:black; }
|
94
|
+
.do { color:#970; }
|
95
|
+
.ds { color:#D42; font-weight:bold; }
|
96
|
+
.e { color:#666; font-weight:bold; }
|
97
|
+
.er { color:#F00; background-color:#FAA; }
|
98
|
+
.ex { color:#F00; font-weight:bold; }
|
99
|
+
.fl { color:#60E; font-weight:bold; }
|
100
|
+
.fu { color:#06B; font-weight:bold; }
|
101
|
+
.gv { color:#d70; font-weight:bold; }
|
102
|
+
.hx { color:#058; font-weight:bold; }
|
103
|
+
.i { color:#00D; font-weight:bold; }
|
104
|
+
.ic { color:#B44; font-weight:bold; }
|
105
|
+
.in { color:#B2B; font-weight:bold; }
|
106
|
+
.iv { color:#33B; }
|
107
|
+
.la { color:#970; font-weight:bold; }
|
108
|
+
.lv { color:#963; }
|
109
|
+
.oc { color:#40E; font-weight:bold; }
|
110
|
+
.on { color:#000; font-weight:bold; }
|
111
|
+
.pc { color:#038; font-weight:bold; }
|
112
|
+
.pd { color:#369; font-weight:bold; }
|
113
|
+
.pp { color:#579; }
|
114
|
+
.pt { color:#339; font-weight:bold; }
|
115
|
+
.r { color:#080; font-weight:bold; }
|
116
|
+
|
117
|
+
.rx { background-color:#fff0ff; }
|
118
|
+
.rx .k { color:#808; }
|
119
|
+
.rx .dl { color:#404; }
|
120
|
+
.rx .mod { color:#C2C; }
|
121
|
+
.rx .fu { color:#404; font-weight: bold; }
|
122
|
+
|
123
|
+
.s { background-color:#fff0f0; }
|
124
|
+
.s .s { background-color:#ffe0e0; }
|
125
|
+
.s .s .s { background-color:#ffd0d0; }
|
126
|
+
.s .k { color:#D20; }
|
127
|
+
.s .dl { color:#710; }
|
128
|
+
|
129
|
+
.sh { background-color:#f0fff0; }
|
130
|
+
.sh .k { color:#2B2; }
|
131
|
+
.sh .dl { color:#161; }
|
132
|
+
|
133
|
+
.sy { color:#A60; }
|
134
|
+
.sy .k { color:#A60; }
|
135
|
+
.sy .dl { color:#630; }
|
136
|
+
|
137
|
+
.ta { color:#070; }
|
138
|
+
.tf { color:#070; font-weight:bold; }
|
139
|
+
.ts { color:#D70; font-weight:bold; }
|
140
|
+
.ty { color:#339; font-weight:bold; }
|
141
|
+
.v { color:#036; }
|
142
|
+
.xt { color:#444; }
|
143
|
+
TOKENS
|
144
|
+
|
145
|
+
DEFAULT_STYLESHEET = MAIN + TOKENS.gsub(/^(?!$)/, '.CodeRay ')
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end end
|
151
|
+
|
152
|
+
if $0 == __FILE__
|
153
|
+
require 'pp'
|
154
|
+
pp CodeRay::Encoders::HTML::CSS.new
|
155
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module CodeRay module Encoders
|
2
|
+
|
3
|
+
class HTML
|
4
|
+
|
5
|
+
ClassOfKind = {
|
6
|
+
:attribute_name => 'an',
|
7
|
+
:attribute_name_fat => 'af',
|
8
|
+
:attribute_value => 'av',
|
9
|
+
:attribute_value_fat => 'aw',
|
10
|
+
:bin => 'bi',
|
11
|
+
:char => 'ch',
|
12
|
+
:class => 'cl',
|
13
|
+
:class_variable => 'cv',
|
14
|
+
:color => 'cr',
|
15
|
+
:comment => 'c',
|
16
|
+
:constant => 'co',
|
17
|
+
:content => 'k',
|
18
|
+
:definition => 'df',
|
19
|
+
:delimiter => 'dl',
|
20
|
+
:directive => 'di',
|
21
|
+
:doc => 'do',
|
22
|
+
:doc_string => 'ds',
|
23
|
+
:error => 'er',
|
24
|
+
:escape => 'e',
|
25
|
+
:exception => 'ex',
|
26
|
+
:float => 'fl',
|
27
|
+
:function => 'fu',
|
28
|
+
:global_variable => 'gv',
|
29
|
+
:hex => 'hx',
|
30
|
+
:include => 'ic',
|
31
|
+
:instance_variable => 'iv',
|
32
|
+
:integer => 'i',
|
33
|
+
:interpreted => 'in',
|
34
|
+
:label => 'la',
|
35
|
+
:local_variable => 'lv',
|
36
|
+
:modifier => 'mod',
|
37
|
+
:oct => 'oc',
|
38
|
+
:operator_name => 'on',
|
39
|
+
:pre_constant => 'pc',
|
40
|
+
:pre_type => 'pt',
|
41
|
+
:predefined => 'pd',
|
42
|
+
:preprocessor => 'pp',
|
43
|
+
:regexp => 'rx',
|
44
|
+
:reserved => 'r',
|
45
|
+
:shell => 'sh',
|
46
|
+
:string => 's',
|
47
|
+
:symbol => 'sy',
|
48
|
+
:tag => 'ta',
|
49
|
+
:tag_fat => 'tf',
|
50
|
+
:tag_special => 'ts',
|
51
|
+
:type => 'ty',
|
52
|
+
:variable => 'v',
|
53
|
+
:xml_text => 'xt',
|
54
|
+
|
55
|
+
:ident => :NO_HIGHLIGHT, # 'id'
|
56
|
+
:operator => :NO_HIGHLIGHT, # 'op'
|
57
|
+
:space => :NO_HIGHLIGHT, # 'sp'
|
58
|
+
:plain => :NO_HIGHLIGHT,
|
59
|
+
}
|
60
|
+
ClassOfKind[:procedure] = ClassOfKind[:method] = ClassOfKind[:function]
|
61
|
+
ClassOfKind[:open] = ClassOfKind[:close] = ClassOfKind[:delimiter]
|
62
|
+
ClassOfKind[:nesting_delimiter] = ClassOfKind[:delimiter]
|
63
|
+
ClassOfKind[:escape] = ClassOfKind[:delimiter]
|
64
|
+
ClassOfKind.default = ClassOfKind[:error] or raise 'no class found for :error!'
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
module CodeRay
|
2
|
+
module Encoders
|
3
|
+
|
4
|
+
class HTML
|
5
|
+
|
6
|
+
# This module is included in the output String from thew HTML Encoder.
|
7
|
+
#
|
8
|
+
# It provides methods like wrap, div, page etc.
|
9
|
+
#
|
10
|
+
# Remember to use #clone instead of #dup to keep the modules the object was
|
11
|
+
# extended with.
|
12
|
+
#
|
13
|
+
# TODO: more doc.
|
14
|
+
module Output
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# This makes Output look like a class.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
#
|
22
|
+
# a = Output.new '<span class="co">Code</span>'
|
23
|
+
# a.wrap! :page
|
24
|
+
def new string, element = nil
|
25
|
+
output = string.clone.extend self
|
26
|
+
output.wrapped_in = element
|
27
|
+
output
|
28
|
+
end
|
29
|
+
|
30
|
+
# Raises an exception if an object that doesn't respond to to_str is extended by Output,
|
31
|
+
# to prevent users from misuse. Use Module#remove_method to disable.
|
32
|
+
def extended o
|
33
|
+
warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str
|
34
|
+
end
|
35
|
+
|
36
|
+
def page_template_for_css css = :default
|
37
|
+
css = CSS::DEFAULT_STYLESHEET if css == :default
|
38
|
+
PAGE.apply 'CSS', css
|
39
|
+
end
|
40
|
+
|
41
|
+
# Define a new wrapper. This is meta programming.
|
42
|
+
def wrapper *wrappers
|
43
|
+
wrappers.each do |wrapper|
|
44
|
+
define_method wrapper do |*args|
|
45
|
+
wrap wrapper, *args
|
46
|
+
end
|
47
|
+
define_method "#{wrapper}!".to_sym do |*args|
|
48
|
+
wrap! wrapper, *args
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
wrapper :div, :span, :page
|
55
|
+
|
56
|
+
def wrapped_in
|
57
|
+
@wrapped_in ||= nil
|
58
|
+
end
|
59
|
+
attr_writer :wrapped_in
|
60
|
+
|
61
|
+
def wrapped_in? element
|
62
|
+
wrapped_in == element
|
63
|
+
end
|
64
|
+
|
65
|
+
def wrap_in template
|
66
|
+
clone.wrap_in! template
|
67
|
+
end
|
68
|
+
|
69
|
+
def wrap_in! template
|
70
|
+
Template.wrap! self, template, 'CONTENT'
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def wrap! element, *args
|
75
|
+
return self if not element or element == wrapped_in
|
76
|
+
case element
|
77
|
+
when :div
|
78
|
+
raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
|
79
|
+
wrap_in! DIV
|
80
|
+
when :span
|
81
|
+
raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
|
82
|
+
wrap_in! SPAN
|
83
|
+
when :page
|
84
|
+
wrap! :div if wrapped_in? nil
|
85
|
+
raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div
|
86
|
+
wrap_in! Output.page_template_for_css
|
87
|
+
when nil
|
88
|
+
return self
|
89
|
+
else
|
90
|
+
raise "Unknown value %p for :wrap" % element
|
91
|
+
end
|
92
|
+
@wrapped_in = element
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def wrap *args
|
97
|
+
clone.wrap!(*args)
|
98
|
+
end
|
99
|
+
|
100
|
+
def numerize! mode = :table, options = {}
|
101
|
+
return self unless mode
|
102
|
+
|
103
|
+
start = options.fetch :line_number_start, DEFAULT_OPTIONS[:line_number_start]
|
104
|
+
unless start.is_a? Integer
|
105
|
+
raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start
|
106
|
+
end
|
107
|
+
|
108
|
+
unless NUMERIZABLE_WRAPPINGS.include? options[:wrap]
|
109
|
+
raise ArgumentError, "Can't numerize, :wrap must be in %p, but is %p" % [NUMERIZABLE_WRAPPINGS, options[:wrap]]
|
110
|
+
end
|
111
|
+
|
112
|
+
bold_every = options.fetch :bold_every, DEFAULT_OPTIONS[:bold_every]
|
113
|
+
bolding =
|
114
|
+
if bold_every == :no_bolding or bold_every == 0
|
115
|
+
proc { |line| line.to_s }
|
116
|
+
elsif bold_every.is_a? Integer
|
117
|
+
proc do |line|
|
118
|
+
if line % bold_every == 0
|
119
|
+
"<strong>#{line}</strong>" # every bold_every-th number in bold
|
120
|
+
else
|
121
|
+
line.to_s
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
raise ArgumentError, "Invalid value %p for :bolding; :no_bolding or Integer expected." % bolding
|
126
|
+
end
|
127
|
+
|
128
|
+
line_count = count("\n")
|
129
|
+
line_count += 1 if self[-1] != ?\n
|
130
|
+
|
131
|
+
case mode
|
132
|
+
when :inline
|
133
|
+
max_width = (start + line_count).to_s.size
|
134
|
+
line = start
|
135
|
+
gsub!(/^/) do
|
136
|
+
line_number = bolding.call line
|
137
|
+
line += 1
|
138
|
+
"<span class=\"no\">#{ line_number.rjust(max_width) }</span> "
|
139
|
+
end
|
140
|
+
wrap! :div
|
141
|
+
|
142
|
+
when :table
|
143
|
+
# This is really ugly.
|
144
|
+
# Because even monospace fonts seem to have different heights when bold,
|
145
|
+
# I make the newline bold, both in the code and the line numbers.
|
146
|
+
# FIXME Still not working perfect for Mr. Internet Exploder
|
147
|
+
line_numbers = (start ... start + line_count).to_a.map(&bolding).join("\n")
|
148
|
+
line_numbers << "\n" # also for Mr. MS Internet Exploder :-/
|
149
|
+
line_numbers.gsub!(/\n/) { "<tt>\n</tt>" }
|
150
|
+
|
151
|
+
line_numbers_tpl = DIV_TABLE.apply('LINE_NUMBERS', line_numbers)
|
152
|
+
gsub!(/\n/) { "<tt>\n</tt>" }
|
153
|
+
wrap_in! line_numbers_tpl
|
154
|
+
@wrapped_in = :div
|
155
|
+
|
156
|
+
else
|
157
|
+
raise ArgumentError, "Unknown value %p for mode: :inline or :table expected" % mode
|
158
|
+
end
|
159
|
+
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
def numerize *args
|
164
|
+
clone.numerize!(*args)
|
165
|
+
end
|
166
|
+
|
167
|
+
class Template < String
|
168
|
+
|
169
|
+
def self.wrap! str, template, target
|
170
|
+
target = Regexp.new(Regexp.escape("<%#{target}%>"))
|
171
|
+
if template =~ target
|
172
|
+
str[0,0] = $`
|
173
|
+
str << $'
|
174
|
+
else
|
175
|
+
raise "Template target <%%%p%%> not found" % target
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def apply target, replacement
|
180
|
+
target = Regexp.new(Regexp.escape("<%#{target}%>"))
|
181
|
+
if self =~ target
|
182
|
+
Template.new($` + replacement + $')
|
183
|
+
else
|
184
|
+
raise "Template target <%%%p%%> not found" % target
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
module Simple
|
189
|
+
def ` str #`
|
190
|
+
Template.new str
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
extend Template::Simple
|
196
|
+
|
197
|
+
#-- don't include the templates in docu
|
198
|
+
|
199
|
+
SPAN = `<span class="CodeRay"><%CONTENT%></span>`
|
200
|
+
|
201
|
+
DIV = <<-`DIV`
|
202
|
+
<div class="CodeRay">
|
203
|
+
<div class="code"><pre><%CONTENT%></pre></div>
|
204
|
+
</div>
|
205
|
+
DIV
|
206
|
+
|
207
|
+
DIV_TABLE = <<-`DIV_TABLE`
|
208
|
+
<table class="CodeRay"> <tr>
|
209
|
+
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><%LINE_NUMBERS%></pre></td>
|
210
|
+
<td class="code"><pre title="double click to expand" ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><%CONTENT%></pre></td>
|
211
|
+
</tr> </table>
|
212
|
+
DIV_TABLE
|
213
|
+
|
214
|
+
PAGE = <<-`PAGE`
|
215
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
216
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
217
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="de">
|
218
|
+
<head>
|
219
|
+
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
|
220
|
+
<title>CodeRay HTML Encoder Example</title>
|
221
|
+
<style type="text/css">
|
222
|
+
<%CSS%>
|
223
|
+
</style>
|
224
|
+
</head>
|
225
|
+
<body style="background-color: white;">
|
226
|
+
|
227
|
+
<%CONTENT%>
|
228
|
+
</body>
|
229
|
+
</html>
|
230
|
+
PAGE
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|