gitlab-rouge 1.9.2

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.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +23 -0
  3. data/LICENSE +186 -0
  4. data/bin/rougify +16 -0
  5. data/gitlab-rouge.gemspec +17 -0
  6. data/lib/rouge.rb +57 -0
  7. data/lib/rouge/cli.rb +363 -0
  8. data/lib/rouge/demos/apache +21 -0
  9. data/lib/rouge/demos/applescript +2 -0
  10. data/lib/rouge/demos/c +8 -0
  11. data/lib/rouge/demos/clojure +5 -0
  12. data/lib/rouge/demos/coffeescript +5 -0
  13. data/lib/rouge/demos/common_lisp +1 -0
  14. data/lib/rouge/demos/conf +4 -0
  15. data/lib/rouge/demos/cpp +8 -0
  16. data/lib/rouge/demos/csharp +5 -0
  17. data/lib/rouge/demos/css +4 -0
  18. data/lib/rouge/demos/dart +6 -0
  19. data/lib/rouge/demos/diff +7 -0
  20. data/lib/rouge/demos/elixir +1 -0
  21. data/lib/rouge/demos/erb +1 -0
  22. data/lib/rouge/demos/erlang +7 -0
  23. data/lib/rouge/demos/factor +5 -0
  24. data/lib/rouge/demos/gherkin +17 -0
  25. data/lib/rouge/demos/glsl +14 -0
  26. data/lib/rouge/demos/go +7 -0
  27. data/lib/rouge/demos/groovy +9 -0
  28. data/lib/rouge/demos/haml +5 -0
  29. data/lib/rouge/demos/handlebars +7 -0
  30. data/lib/rouge/demos/haskell +6 -0
  31. data/lib/rouge/demos/html +8 -0
  32. data/lib/rouge/demos/http +14 -0
  33. data/lib/rouge/demos/ini +4 -0
  34. data/lib/rouge/demos/io +11 -0
  35. data/lib/rouge/demos/java +5 -0
  36. data/lib/rouge/demos/javascript +1 -0
  37. data/lib/rouge/demos/json +1 -0
  38. data/lib/rouge/demos/json-doc +1 -0
  39. data/lib/rouge/demos/liquid +11 -0
  40. data/lib/rouge/demos/literate_coffeescript +3 -0
  41. data/lib/rouge/demos/literate_haskell +7 -0
  42. data/lib/rouge/demos/llvm +20 -0
  43. data/lib/rouge/demos/lua +12 -0
  44. data/lib/rouge/demos/make +6 -0
  45. data/lib/rouge/demos/markdown +4 -0
  46. data/lib/rouge/demos/matlab +6 -0
  47. data/lib/rouge/demos/moonscript +16 -0
  48. data/lib/rouge/demos/nginx +5 -0
  49. data/lib/rouge/demos/nim +27 -0
  50. data/lib/rouge/demos/objective_c +14 -0
  51. data/lib/rouge/demos/ocaml +12 -0
  52. data/lib/rouge/demos/perl +5 -0
  53. data/lib/rouge/demos/php +3 -0
  54. data/lib/rouge/demos/plaintext +1 -0
  55. data/lib/rouge/demos/powershell +49 -0
  56. data/lib/rouge/demos/prolog +9 -0
  57. data/lib/rouge/demos/properties +7 -0
  58. data/lib/rouge/demos/puppet +6 -0
  59. data/lib/rouge/demos/python +6 -0
  60. data/lib/rouge/demos/qml +9 -0
  61. data/lib/rouge/demos/r +8 -0
  62. data/lib/rouge/demos/racket +24 -0
  63. data/lib/rouge/demos/ruby +9 -0
  64. data/lib/rouge/demos/rust +12 -0
  65. data/lib/rouge/demos/sass +3 -0
  66. data/lib/rouge/demos/scala +3 -0
  67. data/lib/rouge/demos/scheme +4 -0
  68. data/lib/rouge/demos/scss +5 -0
  69. data/lib/rouge/demos/sed +4 -0
  70. data/lib/rouge/demos/shell +2 -0
  71. data/lib/rouge/demos/slim +17 -0
  72. data/lib/rouge/demos/smalltalk +6 -0
  73. data/lib/rouge/demos/sml +4 -0
  74. data/lib/rouge/demos/sql +1 -0
  75. data/lib/rouge/demos/swift +5 -0
  76. data/lib/rouge/demos/tcl +1 -0
  77. data/lib/rouge/demos/tex +1 -0
  78. data/lib/rouge/demos/toml +9 -0
  79. data/lib/rouge/demos/tulip +14 -0
  80. data/lib/rouge/demos/vb +4 -0
  81. data/lib/rouge/demos/viml +5 -0
  82. data/lib/rouge/demos/xml +2 -0
  83. data/lib/rouge/demos/yaml +4 -0
  84. data/lib/rouge/formatter.rb +50 -0
  85. data/lib/rouge/formatters/html.rb +117 -0
  86. data/lib/rouge/formatters/null.rb +19 -0
  87. data/lib/rouge/formatters/terminal256.rb +176 -0
  88. data/lib/rouge/lexer.rb +443 -0
  89. data/lib/rouge/lexers/apache.rb +68 -0
  90. data/lib/rouge/lexers/apache/keywords.yml +453 -0
  91. data/lib/rouge/lexers/apple_script.rb +367 -0
  92. data/lib/rouge/lexers/c.rb +212 -0
  93. data/lib/rouge/lexers/clojure.rb +112 -0
  94. data/lib/rouge/lexers/coffeescript.rb +174 -0
  95. data/lib/rouge/lexers/common_lisp.rb +345 -0
  96. data/lib/rouge/lexers/conf.rb +24 -0
  97. data/lib/rouge/lexers/cpp.rb +66 -0
  98. data/lib/rouge/lexers/csharp.rb +88 -0
  99. data/lib/rouge/lexers/css.rb +271 -0
  100. data/lib/rouge/lexers/dart.rb +104 -0
  101. data/lib/rouge/lexers/diff.rb +31 -0
  102. data/lib/rouge/lexers/elixir.rb +108 -0
  103. data/lib/rouge/lexers/erb.rb +56 -0
  104. data/lib/rouge/lexers/erlang.rb +118 -0
  105. data/lib/rouge/lexers/factor.rb +302 -0
  106. data/lib/rouge/lexers/gherkin.rb +137 -0
  107. data/lib/rouge/lexers/gherkin/keywords.rb +14 -0
  108. data/lib/rouge/lexers/glsl.rb +135 -0
  109. data/lib/rouge/lexers/go.rb +178 -0
  110. data/lib/rouge/lexers/groovy.rb +104 -0
  111. data/lib/rouge/lexers/haml.rb +228 -0
  112. data/lib/rouge/lexers/handlebars.rb +79 -0
  113. data/lib/rouge/lexers/haskell.rb +183 -0
  114. data/lib/rouge/lexers/html.rb +94 -0
  115. data/lib/rouge/lexers/http.rb +80 -0
  116. data/lib/rouge/lexers/ini.rb +57 -0
  117. data/lib/rouge/lexers/io.rb +68 -0
  118. data/lib/rouge/lexers/java.rb +76 -0
  119. data/lib/rouge/lexers/javascript.rb +297 -0
  120. data/lib/rouge/lexers/liquid.rb +287 -0
  121. data/lib/rouge/lexers/literate_coffeescript.rb +33 -0
  122. data/lib/rouge/lexers/literate_haskell.rb +36 -0
  123. data/lib/rouge/lexers/llvm.rb +84 -0
  124. data/lib/rouge/lexers/lua.rb +122 -0
  125. data/lib/rouge/lexers/lua/builtins.rb +22 -0
  126. data/lib/rouge/lexers/make.rb +116 -0
  127. data/lib/rouge/lexers/markdown.rb +154 -0
  128. data/lib/rouge/lexers/matlab.rb +74 -0
  129. data/lib/rouge/lexers/matlab/builtins.rb +11 -0
  130. data/lib/rouge/lexers/moonscript.rb +110 -0
  131. data/lib/rouge/lexers/nginx.rb +71 -0
  132. data/lib/rouge/lexers/nim.rb +152 -0
  133. data/lib/rouge/lexers/objective_c.rb +197 -0
  134. data/lib/rouge/lexers/ocaml.rb +111 -0
  135. data/lib/rouge/lexers/perl.rb +197 -0
  136. data/lib/rouge/lexers/php.rb +173 -0
  137. data/lib/rouge/lexers/php/builtins.rb +204 -0
  138. data/lib/rouge/lexers/plain_text.rb +25 -0
  139. data/lib/rouge/lexers/powershell.rb +96 -0
  140. data/lib/rouge/lexers/prolog.rb +64 -0
  141. data/lib/rouge/lexers/properties.rb +55 -0
  142. data/lib/rouge/lexers/puppet.rb +128 -0
  143. data/lib/rouge/lexers/python.rb +228 -0
  144. data/lib/rouge/lexers/qml.rb +72 -0
  145. data/lib/rouge/lexers/r.rb +56 -0
  146. data/lib/rouge/lexers/racket.rb +542 -0
  147. data/lib/rouge/lexers/ruby.rb +415 -0
  148. data/lib/rouge/lexers/rust.rb +191 -0
  149. data/lib/rouge/lexers/sass.rb +74 -0
  150. data/lib/rouge/lexers/sass/common.rb +180 -0
  151. data/lib/rouge/lexers/scala.rb +142 -0
  152. data/lib/rouge/lexers/scheme.rb +112 -0
  153. data/lib/rouge/lexers/scss.rb +34 -0
  154. data/lib/rouge/lexers/sed.rb +170 -0
  155. data/lib/rouge/lexers/shell.rb +152 -0
  156. data/lib/rouge/lexers/slim.rb +228 -0
  157. data/lib/rouge/lexers/smalltalk.rb +116 -0
  158. data/lib/rouge/lexers/sml.rb +347 -0
  159. data/lib/rouge/lexers/sql.rb +140 -0
  160. data/lib/rouge/lexers/swift.rb +144 -0
  161. data/lib/rouge/lexers/tcl.rb +192 -0
  162. data/lib/rouge/lexers/tex.rb +72 -0
  163. data/lib/rouge/lexers/toml.rb +71 -0
  164. data/lib/rouge/lexers/tulip.rb +75 -0
  165. data/lib/rouge/lexers/vb.rb +164 -0
  166. data/lib/rouge/lexers/viml.rb +101 -0
  167. data/lib/rouge/lexers/viml/keywords.rb +12 -0
  168. data/lib/rouge/lexers/xml.rb +59 -0
  169. data/lib/rouge/lexers/yaml.rb +364 -0
  170. data/lib/rouge/plugins/redcarpet.rb +30 -0
  171. data/lib/rouge/regex_lexer.rb +439 -0
  172. data/lib/rouge/template_lexer.rb +22 -0
  173. data/lib/rouge/text_analyzer.rb +48 -0
  174. data/lib/rouge/theme.rb +195 -0
  175. data/lib/rouge/themes/base16.rb +130 -0
  176. data/lib/rouge/themes/colorful.rb +67 -0
  177. data/lib/rouge/themes/github.rb +71 -0
  178. data/lib/rouge/themes/molokai.rb +82 -0
  179. data/lib/rouge/themes/monokai.rb +92 -0
  180. data/lib/rouge/themes/monokai_sublime.rb +90 -0
  181. data/lib/rouge/themes/thankful_eyes.rb +71 -0
  182. data/lib/rouge/token.rb +182 -0
  183. data/lib/rouge/util.rb +101 -0
  184. data/lib/rouge/version.rb +7 -0
  185. metadata +231 -0
@@ -0,0 +1,443 @@
1
+ # -*- coding: utf-8 -*- #
2
+
3
+ # stdlib
4
+ require 'strscan'
5
+ require 'cgi'
6
+ require 'set'
7
+
8
+ module Rouge
9
+ # @abstract
10
+ # A lexer transforms text into a stream of `[token, chunk]` pairs.
11
+ class Lexer
12
+ include Token::Tokens
13
+
14
+ class << self
15
+ # Lexes `stream` with the given options. The lex is delegated to a
16
+ # new instance.
17
+ #
18
+ # @see #lex
19
+ def lex(stream, opts={}, &b)
20
+ new(opts).lex(stream, &b)
21
+ end
22
+
23
+ def default_options(o={})
24
+ @default_options ||= {}
25
+ @default_options.merge!(o)
26
+ @default_options
27
+ end
28
+
29
+ # Given a string, return the correct lexer class.
30
+ def find(name)
31
+ registry[name.to_s]
32
+ end
33
+
34
+ # Find a lexer, with fancy shiny features.
35
+ #
36
+ # * The string you pass can include CGI-style options
37
+ #
38
+ # Lexer.find_fancy('erb?parent=tex')
39
+ #
40
+ # * You can pass the special name 'guess' so we guess for you,
41
+ # and you can pass a second argument of the code to guess by
42
+ #
43
+ # Lexer.find_fancy('guess', "#!/bin/bash\necho Hello, world")
44
+ #
45
+ # This is used in the Redcarpet plugin as well as Rouge's own
46
+ # markdown lexer for highlighting internal code blocks.
47
+ #
48
+ def find_fancy(str, code=nil)
49
+ name, opts = str ? str.split('?', 2) : [nil, '']
50
+
51
+ # parse the options hash from a cgi-style string
52
+ opts = CGI.parse(opts || '').map do |k, vals|
53
+ [ k.to_sym, vals.empty? ? true : vals[0] ]
54
+ end
55
+
56
+ opts = Hash[opts]
57
+
58
+ lexer_class = case name
59
+ when 'guess', nil
60
+ self.guess(:source => code, :mimetype => opts[:mimetype])
61
+ when String
62
+ self.find(name)
63
+ end
64
+
65
+ lexer_class && lexer_class.new(opts)
66
+ end
67
+
68
+ # Specify or get this lexer's title. Meant to be human-readable.
69
+ def title(t=nil)
70
+ if t.nil?
71
+ t = tag.capitalize
72
+ end
73
+ @title ||= t
74
+ end
75
+
76
+ # Specify or get this lexer's description.
77
+ def desc(arg=:absent)
78
+ if arg == :absent
79
+ @desc
80
+ else
81
+ @desc = arg
82
+ end
83
+ end
84
+
85
+ # Specify or get the path name containing a small demo for
86
+ # this lexer (can be overriden by {demo}).
87
+ def demo_file(arg=:absent)
88
+ return @demo_file = Pathname.new(arg) unless arg == :absent
89
+
90
+ @demo_file = Pathname.new(__FILE__).dirname.join('demos', tag)
91
+ end
92
+
93
+ # Specify or get a small demo string for this lexer
94
+ def demo(arg=:absent)
95
+ return @demo = arg unless arg == :absent
96
+
97
+ @demo = File.read(demo_file, encoding: 'utf-8')
98
+ end
99
+
100
+ # @return a list of all lexers.
101
+ def all
102
+ registry.values.uniq
103
+ end
104
+
105
+ # Guess which lexer to use based on a hash of info.
106
+ #
107
+ # This accepts the same arguments as Lexer.guess, but will never throw
108
+ # an error. It will return a (possibly empty) list of potential lexers
109
+ # to use.
110
+ def guesses(info={})
111
+ mimetype, filename, source = info.values_at(:mimetype, :filename, :source)
112
+ lexers = registry.values.uniq
113
+ total_size = lexers.size
114
+
115
+ lexers = filter_by_mimetype(lexers, mimetype) if mimetype
116
+ return lexers if lexers.size == 1
117
+
118
+ lexers = filter_by_filename(lexers, filename) if filename
119
+ return lexers if lexers.size == 1
120
+
121
+ if source
122
+ # If we're filtering against *all* lexers, we only use confident return
123
+ # values from analyze_text. But if we've filtered down already, we can trust
124
+ # the analysis more.
125
+ source_threshold = lexers.size < total_size ? 0 : 0.5
126
+ return [best_by_source(lexers, source, source_threshold)].compact
127
+ end
128
+
129
+ []
130
+ end
131
+
132
+ class AmbiguousGuess < StandardError
133
+ attr_reader :alternatives
134
+ def initialize(alternatives); @alternatives = alternatives; end
135
+
136
+ def message
137
+ "Ambiguous guess: can't decide between #{alternatives.map(&:tag).inspect}"
138
+ end
139
+ end
140
+
141
+ # Guess which lexer to use based on a hash of info.
142
+ #
143
+ # @option info :mimetype
144
+ # A mimetype to guess by
145
+ # @option info :filename
146
+ # A filename to guess by
147
+ # @option info :source
148
+ # The source itself, which, if guessing by mimetype or filename
149
+ # fails, will be searched for shebangs, <!DOCTYPE ...> tags, and
150
+ # other hints.
151
+ #
152
+ # @see Lexer.analyze_text
153
+ # @see Lexer.guesses
154
+ def guess(info={})
155
+ lexers = guesses(info)
156
+
157
+ return Lexers::PlainText if lexers.empty?
158
+ return lexers[0] if lexers.size == 1
159
+
160
+ raise AmbiguousGuess.new(lexers)
161
+ end
162
+
163
+ def guess_by_mimetype(mt)
164
+ guess :mimetype => mt
165
+ end
166
+
167
+ def guess_by_filename(fname)
168
+ guess :filename => fname
169
+ end
170
+
171
+ def guess_by_source(source)
172
+ guess :source => source
173
+ end
174
+
175
+ private
176
+ def filter_by_mimetype(lexers, mt)
177
+ filtered = lexers.select { |lexer| lexer.mimetypes.include? mt }
178
+ filtered.any? ? filtered : lexers
179
+ end
180
+
181
+ # returns a list of lexers that match the given filename with
182
+ # equal specificity (i.e. number of wildcards in the pattern).
183
+ # This helps disambiguate between, e.g. the Nginx lexer, which
184
+ # matches `nginx.conf`, and the Conf lexer, which matches `*.conf`.
185
+ # In this case, nginx will win because the pattern has no wildcards,
186
+ # while `*.conf` has one.
187
+ def filter_by_filename(lexers, fname)
188
+ fname = File.basename(fname)
189
+
190
+ out = []
191
+ best_seen = nil
192
+ lexers.each do |lexer|
193
+ score = lexer.filenames.map do |pattern|
194
+ if File.fnmatch?(pattern, fname, File::FNM_DOTMATCH)
195
+ # specificity is better the fewer wildcards there are
196
+ pattern.scan(/[*?\[]/).size
197
+ end
198
+ end.compact.min
199
+
200
+ next unless score
201
+
202
+ if best_seen.nil? || score < best_seen
203
+ best_seen = score
204
+ out = [lexer]
205
+ elsif score == best_seen
206
+ out << lexer
207
+ end
208
+ end
209
+
210
+ out.any? ? out : lexers
211
+ end
212
+
213
+ def best_by_source(lexers, source, threshold=0)
214
+ source = case source
215
+ when String
216
+ source
217
+ when ->(s){ s.respond_to? :read }
218
+ source.read
219
+ else
220
+ raise 'invalid source'
221
+ end
222
+
223
+ assert_utf8!(source)
224
+
225
+ source = TextAnalyzer.new(source)
226
+
227
+ best_result = threshold
228
+ best_match = nil
229
+ lexers.each do |lexer|
230
+ result = lexer.analyze_text(source) || 0
231
+ return lexer if result == 1
232
+
233
+ if result > best_result
234
+ best_match = lexer
235
+ best_result = result
236
+ end
237
+ end
238
+
239
+ best_match
240
+ end
241
+
242
+ protected
243
+ # @private
244
+ def register(name, lexer)
245
+ registry[name.to_s] = lexer
246
+ end
247
+
248
+ public
249
+ # Used to specify or get the canonical name of this lexer class.
250
+ #
251
+ # @example
252
+ # class MyLexer < Lexer
253
+ # tag 'foo'
254
+ # end
255
+ #
256
+ # MyLexer.tag # => 'foo'
257
+ #
258
+ # Lexer.find('foo') # => MyLexer
259
+ def tag(t=nil)
260
+ return @tag if t.nil?
261
+
262
+ @tag = t.to_s
263
+ Lexer.register(@tag, self)
264
+ end
265
+
266
+ # Used to specify alternate names this lexer class may be found by.
267
+ #
268
+ # @example
269
+ # class Erb < Lexer
270
+ # tag 'erb'
271
+ # aliases 'eruby', 'rhtml'
272
+ # end
273
+ #
274
+ # Lexer.find('eruby') # => Erb
275
+ def aliases(*args)
276
+ args.map!(&:to_s)
277
+ args.each { |arg| Lexer.register(arg, self) }
278
+ (@aliases ||= []).concat(args)
279
+ end
280
+
281
+ # Specify a list of filename globs associated with this lexer.
282
+ #
283
+ # @example
284
+ # class Ruby < Lexer
285
+ # filenames '*.rb', '*.ruby', 'Gemfile', 'Rakefile'
286
+ # end
287
+ def filenames(*fnames)
288
+ (@filenames ||= []).concat(fnames)
289
+ end
290
+
291
+ # Specify a list of mimetypes associated with this lexer.
292
+ #
293
+ # @example
294
+ # class Html < Lexer
295
+ # mimetypes 'text/html', 'application/xhtml+xml'
296
+ # end
297
+ def mimetypes(*mts)
298
+ (@mimetypes ||= []).concat(mts)
299
+ end
300
+
301
+ # @private
302
+ def assert_utf8!(str)
303
+ return if %w(US-ASCII UTF-8 ASCII-8BIT).include? str.encoding.name
304
+ raise EncodingError.new(
305
+ "Bad encoding: #{str.encoding.names.join(',')}. " +
306
+ "Please convert your string to UTF-8."
307
+ )
308
+ end
309
+
310
+ private
311
+ def registry
312
+ @registry ||= {}
313
+ end
314
+ end
315
+
316
+ # -*- instance methods -*- #
317
+
318
+ # Create a new lexer with the given options. Individual lexers may
319
+ # specify extra options. The only current globally accepted option
320
+ # is `:debug`.
321
+ #
322
+ # @option opts :debug
323
+ # Prints debug information to stdout. The particular info depends
324
+ # on the lexer in question. In regex lexers, this will log the
325
+ # state stack at the beginning of each step, along with each regex
326
+ # tried and each stream consumed. Try it, it's pretty useful.
327
+ def initialize(opts={})
328
+ options(opts)
329
+
330
+ @debug = option(:debug)
331
+ end
332
+
333
+ # get and/or specify the options for this lexer.
334
+ def options(o={})
335
+ (@options ||= {}).merge!(o)
336
+
337
+ self.class.default_options.merge(@options)
338
+ end
339
+
340
+ # get or specify one option for this lexer
341
+ def option(k, v=:absent)
342
+ if v == :absent
343
+ options[k]
344
+ else
345
+ options({ k => v })
346
+ end
347
+ end
348
+
349
+ # @deprecated
350
+ # Instead of `debug { "foo" }`, simply `puts "foo" if @debug`.
351
+ #
352
+ # Leave a debug message if the `:debug` option is set. The message
353
+ # is given as a block because some debug messages contain calculated
354
+ # information that is unnecessary for lexing in the real world.
355
+ #
356
+ # Calls to this method should be guarded with "if @debug" for best
357
+ # performance when debugging is turned off.
358
+ #
359
+ # @example
360
+ # debug { "hello, world!" } if @debug
361
+ def debug
362
+ warn "Lexer#debug is deprecated. Simply puts if @debug instead."
363
+ puts yield if @debug
364
+ end
365
+
366
+ # @abstract
367
+ #
368
+ # Called after each lex is finished. The default implementation
369
+ # is a noop.
370
+ def reset!
371
+ end
372
+
373
+ # Given a string, yield [token, chunk] pairs. If no block is given,
374
+ # an enumerator is returned.
375
+ #
376
+ # @option opts :continue
377
+ # Continue the lex from the previous state (i.e. don't call #reset!)
378
+ def lex(string, opts={}, &b)
379
+ return enum_for(:lex, string, opts) unless block_given?
380
+
381
+ Lexer.assert_utf8!(string)
382
+
383
+ reset! unless opts[:continue]
384
+
385
+ # consolidate consecutive tokens of the same type
386
+ last_token = nil
387
+ last_val = nil
388
+ stream_tokens(string) do |tok, val|
389
+ next if val.empty?
390
+
391
+ if tok == last_token
392
+ last_val << val
393
+ next
394
+ end
395
+
396
+ b.call(last_token, last_val) if last_token
397
+ last_token = tok
398
+ last_val = val
399
+ end
400
+
401
+ b.call(last_token, last_val) if last_token
402
+ end
403
+
404
+ # delegated to {Lexer.tag}
405
+ def tag
406
+ self.class.tag
407
+ end
408
+
409
+ # @abstract
410
+ #
411
+ # Yield `[token, chunk]` pairs, given a prepared input stream. This
412
+ # must be implemented.
413
+ #
414
+ # @param [StringScanner] stream
415
+ # the stream
416
+ def stream_tokens(stream, &b)
417
+ raise 'abstract'
418
+ end
419
+
420
+ # @abstract
421
+ #
422
+ # Return a number between 0 and 1 indicating the likelihood that
423
+ # the text given should be lexed with this lexer. The default
424
+ # implementation returns 0. Values under 0.5 will only be used
425
+ # to disambiguate filename or mimetype matches.
426
+ #
427
+ # @param [TextAnalyzer] text
428
+ # the text to be analyzed, with a couple of handy methods on it,
429
+ # like {TextAnalyzer#shebang?} and {TextAnalyzer#doctype?}
430
+ def self.analyze_text(text)
431
+ 0
432
+ end
433
+ end
434
+
435
+ module Lexers
436
+ def self.load_const(const_name, relpath)
437
+ return if const_defined?(const_name)
438
+
439
+ root = Pathname.new(__FILE__).dirname.join('lexers')
440
+ load root.join(relpath)
441
+ end
442
+ end
443
+ end
@@ -0,0 +1,68 @@
1
+ require 'yaml'
2
+
3
+ module Rouge
4
+ module Lexers
5
+ class Apache < RegexLexer
6
+ title "Apache"
7
+ desc 'configuration files for Apache web server'
8
+ tag 'apache'
9
+ mimetypes 'text/x-httpd-conf', 'text/x-apache-conf'
10
+ filenames '.htaccess', 'httpd.conf'
11
+
12
+ class << self
13
+ attr_reader :keywords
14
+ end
15
+ # Load Apache keywords from separate YML file
16
+ @keywords = ::YAML.load(File.open(Pathname.new(__FILE__).dirname.join('apache/keywords.yml')))
17
+
18
+ def name_for_token(token)
19
+ if self.class.keywords[:sections].include? token
20
+ Name::Class
21
+ elsif self.class.keywords[:directives].include? token
22
+ Name::Label
23
+ elsif self.class.keywords[:values].include? token
24
+ Literal::String::Symbol
25
+ end
26
+ end
27
+
28
+ state :whitespace do
29
+ rule /\#.*?\n/, Comment
30
+ rule /[\s\n]+/m, Text
31
+ end
32
+
33
+
34
+ state :root do
35
+ mixin :whitespace
36
+
37
+ rule /(<\/?)(\w+)/ do |m|
38
+ groups Punctuation, name_for_token(m[2])
39
+ push :section
40
+ end
41
+
42
+ rule /\w+/ do |m|
43
+ token name_for_token(m[0])
44
+ push :directive
45
+ end
46
+ end
47
+
48
+ state :section do
49
+ mixin :whitespace
50
+
51
+ # Match section arguments
52
+ rule /([^>]+)?(>\n)/ do |m|
53
+ groups Literal::String::Regex, Punctuation
54
+ pop!
55
+ end
56
+ end
57
+
58
+ state :directive do
59
+ # Match value literals and other directive arguments
60
+ rule /(\w+)*(.*?(\n|$))/ do |m|
61
+ token name_for_token(m[1]), m[1]
62
+ token Text, m[2]
63
+ pop!
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end