Chris@909
|
1 module CodeRay
|
Chris@909
|
2
|
Chris@909
|
3 # This module holds the Encoder class and its subclasses.
|
Chris@909
|
4 # For example, the HTML encoder is named CodeRay::Encoders::HTML
|
Chris@909
|
5 # can be found in coderay/encoders/html.
|
Chris@909
|
6 #
|
Chris@909
|
7 # Encoders also provides methods and constants for the register
|
Chris@909
|
8 # mechanism and the [] method that returns the Encoder class
|
Chris@909
|
9 # belonging to the given format.
|
Chris@909
|
10 module Encoders
|
Chris@909
|
11
|
Chris@909
|
12 extend PluginHost
|
Chris@909
|
13 plugin_path File.dirname(__FILE__), 'encoders'
|
Chris@909
|
14
|
Chris@909
|
15 # = Encoder
|
Chris@909
|
16 #
|
Chris@909
|
17 # The Encoder base class. Together with Scanner and
|
Chris@909
|
18 # Tokens, it forms the highlighting triad.
|
Chris@909
|
19 #
|
Chris@909
|
20 # Encoder instances take a Tokens object and do something with it.
|
Chris@909
|
21 #
|
Chris@909
|
22 # The most common Encoder is surely the HTML encoder
|
Chris@909
|
23 # (CodeRay::Encoders::HTML). It highlights the code in a colorful
|
Chris@909
|
24 # html page.
|
Chris@909
|
25 # If you want the highlighted code in a div or a span instead,
|
Chris@909
|
26 # use its subclasses Div and Span.
|
Chris@909
|
27 class Encoder
|
Chris@909
|
28 extend Plugin
|
Chris@909
|
29 plugin_host Encoders
|
Chris@909
|
30
|
Chris@909
|
31 class << self
|
Chris@909
|
32
|
Chris@909
|
33 # If FILE_EXTENSION isn't defined, this method returns the
|
Chris@909
|
34 # downcase class name instead.
|
Chris@909
|
35 def const_missing sym
|
Chris@909
|
36 if sym == :FILE_EXTENSION
|
Chris@909
|
37 (defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s
|
Chris@909
|
38 else
|
Chris@909
|
39 super
|
Chris@909
|
40 end
|
Chris@909
|
41 end
|
Chris@909
|
42
|
Chris@909
|
43 # The default file extension for output file of this encoder class.
|
Chris@909
|
44 def file_extension
|
Chris@909
|
45 self::FILE_EXTENSION
|
Chris@909
|
46 end
|
Chris@909
|
47
|
Chris@909
|
48 end
|
Chris@909
|
49
|
Chris@909
|
50 # Subclasses are to store their default options in this constant.
|
Chris@909
|
51 DEFAULT_OPTIONS = { }
|
Chris@909
|
52
|
Chris@909
|
53 # The options you gave the Encoder at creating.
|
Chris@909
|
54 attr_accessor :options, :scanner
|
Chris@909
|
55
|
Chris@909
|
56 # Creates a new Encoder.
|
Chris@909
|
57 # +options+ is saved and used for all encode operations, as long
|
Chris@909
|
58 # as you don't overwrite it there by passing additional options.
|
Chris@909
|
59 #
|
Chris@909
|
60 # Encoder objects provide three encode methods:
|
Chris@909
|
61 # - encode simply takes a +code+ string and a +lang+
|
Chris@909
|
62 # - encode_tokens expects a +tokens+ object instead
|
Chris@909
|
63 #
|
Chris@909
|
64 # Each method has an optional +options+ parameter. These are
|
Chris@909
|
65 # added to the options you passed at creation.
|
Chris@909
|
66 def initialize options = {}
|
Chris@909
|
67 @options = self.class::DEFAULT_OPTIONS.merge options
|
Chris@909
|
68 @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false
|
Chris@909
|
69 end
|
Chris@909
|
70
|
Chris@909
|
71 # Encode a Tokens object.
|
Chris@909
|
72 def encode_tokens tokens, options = {}
|
Chris@909
|
73 options = @options.merge options
|
Chris@909
|
74 @scanner = tokens.scanner if tokens.respond_to? :scanner
|
Chris@909
|
75 setup options
|
Chris@909
|
76 compile tokens, options
|
Chris@909
|
77 finish options
|
Chris@909
|
78 end
|
Chris@909
|
79
|
Chris@909
|
80 # Encode the given +code+ using the Scanner for +lang+.
|
Chris@909
|
81 def encode code, lang, options = {}
|
Chris@909
|
82 options = @options.merge options
|
Chris@909
|
83 @scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self)
|
Chris@909
|
84 setup options
|
Chris@909
|
85 @scanner.tokenize
|
Chris@909
|
86 finish options
|
Chris@909
|
87 end
|
Chris@909
|
88
|
Chris@909
|
89 # You can use highlight instead of encode, if that seems
|
Chris@909
|
90 # more clear to you.
|
Chris@909
|
91 alias highlight encode
|
Chris@909
|
92
|
Chris@909
|
93 # The default file extension for this encoder.
|
Chris@909
|
94 def file_extension
|
Chris@909
|
95 self.class.file_extension
|
Chris@909
|
96 end
|
Chris@909
|
97
|
Chris@909
|
98 def << token
|
Chris@909
|
99 unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN
|
Chris@909
|
100 warn 'Using old Tokens#<< interface.'
|
Chris@909
|
101 @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true
|
Chris@909
|
102 end
|
Chris@909
|
103 self.token(*token)
|
Chris@909
|
104 end
|
Chris@909
|
105
|
Chris@909
|
106 # Called with +content+ and +kind+ of the currently scanned token.
|
Chris@909
|
107 # For simple scanners, it's enougth to implement this method.
|
Chris@909
|
108 #
|
Chris@909
|
109 # By default, it calls text_token, begin_group, end_group, begin_line,
|
Chris@909
|
110 # or end_line, depending on the +content+.
|
Chris@909
|
111 def token content, kind
|
Chris@909
|
112 case content
|
Chris@909
|
113 when String
|
Chris@909
|
114 text_token content, kind
|
Chris@909
|
115 when :begin_group
|
Chris@909
|
116 begin_group kind
|
Chris@909
|
117 when :end_group
|
Chris@909
|
118 end_group kind
|
Chris@909
|
119 when :begin_line
|
Chris@909
|
120 begin_line kind
|
Chris@909
|
121 when :end_line
|
Chris@909
|
122 end_line kind
|
Chris@909
|
123 else
|
Chris@909
|
124 raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind]
|
Chris@909
|
125 end
|
Chris@909
|
126 end
|
Chris@909
|
127
|
Chris@909
|
128 # Called for each text token ([text, kind]), where text is a String.
|
Chris@909
|
129 def text_token text, kind
|
Chris@909
|
130 @out << text
|
Chris@909
|
131 end
|
Chris@909
|
132
|
Chris@909
|
133 # Starts a token group with the given +kind+.
|
Chris@909
|
134 def begin_group kind
|
Chris@909
|
135 end
|
Chris@909
|
136
|
Chris@909
|
137 # Ends a token group with the given +kind+.
|
Chris@909
|
138 def end_group kind
|
Chris@909
|
139 end
|
Chris@909
|
140
|
Chris@909
|
141 # Starts a new line token group with the given +kind+.
|
Chris@909
|
142 def begin_line kind
|
Chris@909
|
143 end
|
Chris@909
|
144
|
Chris@909
|
145 # Ends a new line token group with the given +kind+.
|
Chris@909
|
146 def end_line kind
|
Chris@909
|
147 end
|
Chris@909
|
148
|
Chris@909
|
149 protected
|
Chris@909
|
150
|
Chris@909
|
151 # Called with merged options before encoding starts.
|
Chris@909
|
152 # Sets @out to an empty string.
|
Chris@909
|
153 #
|
Chris@909
|
154 # See the HTML Encoder for an example of option caching.
|
Chris@909
|
155 def setup options
|
Chris@909
|
156 @out = get_output(options)
|
Chris@909
|
157 end
|
Chris@909
|
158
|
Chris@909
|
159 def get_output options
|
Chris@909
|
160 options[:out] || ''
|
Chris@909
|
161 end
|
Chris@909
|
162
|
Chris@909
|
163 # Append data.to_s to the output. Returns the argument.
|
Chris@909
|
164 def output data
|
Chris@909
|
165 @out << data.to_s
|
Chris@909
|
166 data
|
Chris@909
|
167 end
|
Chris@909
|
168
|
Chris@909
|
169 # Called with merged options after encoding starts.
|
Chris@909
|
170 # The return value is the result of encoding, typically @out.
|
Chris@909
|
171 def finish options
|
Chris@909
|
172 @out
|
Chris@909
|
173 end
|
Chris@909
|
174
|
Chris@909
|
175 # Do the encoding.
|
Chris@909
|
176 #
|
Chris@909
|
177 # The already created +tokens+ object must be used; it must be a
|
Chris@909
|
178 # Tokens object.
|
Chris@909
|
179 def compile tokens, options = {}
|
Chris@909
|
180 content = nil
|
Chris@909
|
181 for item in tokens
|
Chris@909
|
182 if item.is_a? Array
|
Chris@909
|
183 raise ArgumentError, 'Two-element array tokens are no longer supported.'
|
Chris@909
|
184 end
|
Chris@909
|
185 if content
|
Chris@909
|
186 token content, item
|
Chris@909
|
187 content = nil
|
Chris@909
|
188 else
|
Chris@909
|
189 content = item
|
Chris@909
|
190 end
|
Chris@909
|
191 end
|
Chris@909
|
192 raise 'odd number list for Tokens' if content
|
Chris@909
|
193 end
|
Chris@909
|
194
|
Chris@909
|
195 alias tokens compile
|
Chris@909
|
196 public :tokens
|
Chris@909
|
197
|
Chris@909
|
198 end
|
Chris@909
|
199
|
Chris@909
|
200 end
|
Chris@909
|
201 end
|