Update CodeRay version to 1.0 final (#4264).

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@7618 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Etienne Massip 2011-10-08 13:34:30 +00:00
parent 6ebb5e834c
commit d1efb4f148
115 changed files with 3482 additions and 17507 deletions

View File

@ -55,7 +55,7 @@ Rails::Initializer.run do |config|
config.action_mailer.perform_deliveries = false
config.gem 'rubytree', :lib => 'tree'
config.gem 'coderay', :version => '~>0.9.7'
config.gem 'coderay', :version => '~>1.0.0'
# Load any local configuration that is kept out of source control
# (e.g. gems, patches).

View File

@ -46,7 +46,7 @@ module Redmine
# Highlights +text+ using +language+ syntax
# Should not return outer pre tag
def highlight_by_language(text, language)
::CodeRay.scan(text, language).html(:line_numbers => :inline, :wrap => :span)
::CodeRay.scan(text, language).html(:line_numbers => :inline, :line_number_anchors => false, :wrap => :span)
end
end
end

View File

@ -18,20 +18,22 @@
}
a.new { color: #b73535; }
.CodeRay .c { color:#666; }
.syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 }
.syntaxhl .comment { color:#666; }
.CodeRay .cl { color:#B06; font-weight:bold }
.CodeRay .dl { color:black }
.CodeRay .fu { color:#06B; font-weight:bold }
.syntaxhl .class { color:#B06; font-weight:bold }
.syntaxhl .delimiter { color:black }
.syntaxhl .function { color:#06B; font-weight:bold }
.CodeRay .il { background: #eee }
.CodeRay .il .idl { font-weight: bold; color: #888 }
.syntaxhl .inline { background: #eee }
.syntaxhl .inline .inline-delimiter { font-weight: bold; color: #888 }
.CodeRay .iv { color:#33B }
.CodeRay .r { color:#080; font-weight:bold }
.syntaxhl .instance-variable { color:#33B }
.syntaxhl .reserved { color:#080; font-weight:bold }
.syntaxhl .string { background-color:#fff0f0; color: #D20; }
.syntaxhl .string .delimiter { color:#710 }
.CodeRay .s { background-color:#fff0f0 }
.CodeRay .s .dl { color:#710 }
</style>
</head>
@ -242,7 +244,7 @@ To go live, all you need to add is a database and a web server.
<h2><a name="13" class="wiki-page"></a>Code highlighting</h2>
<p>Code highlightment relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
<p>Default code highlightment relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
<p>You can highlight code in your wiki page using this syntax:</p>
@ -254,17 +256,16 @@ To go live, all you need to add is a database and a web server.
<p>Example:</p>
<pre><code class="ruby CodeRay"><span class="no"> 1</span> <span class="c"># The Greeter class</span>
<span class="no"> 2</span> <span class="r">class</span> <span class="cl">Greeter</span>
<span class="no"> 3</span> <span class="r">def</span> <span class="fu">initialize</span>(name)
<span class="no"> 4</span> <span class="iv">@name</span> = name.capitalize
<span class="no"> 5</span> <span class="r">end</span>
<span class="no"> 6</span>
<span class="no"> 7</span> <span class="r">def</span> <span class="fu">salute</span>
<span class="no"> 8</span> puts <span class="s"><span class="dl">"</span><span class="k">Hello </span><span class="il"><span class="idl">#{</span><span class="iv">@name</span><span class="idl">}</span></span><span class="k">!</span><span class="dl">"</span></span>
<span class="no"> 9</span> <span class="r">end</span>
<span class="no"><strong>10</strong></span> <span class="r">end</span>
</code>
<pre><code class="ruby syntaxhl"><span class="line-numbers"> 1</span> <span class="comment"># The Greeter class</span>
<span class="line-numbers"> 2</span> <span class="reserved">class</span> <span class="class">Greeter</span>
<span class="line-numbers"> 3</span> <span class="reserved">def</span> <span class="function">initialize</span>(name)
<span class="line-numbers"> 4</span> <span class="instance-variable">@name</span> = name.capitalize
<span class="line-numbers"> 5</span> <span class="reserved">end</span>
<span class="line-numbers"> 6</span>
<span class="line-numbers"> 7</span> <span class="reserved">def</span> <span class="function">salute</span>
<span class="line-numbers"> 8</span> puts <span class="string"><span class="delimiter">"</span><span class="content">Hello </span><span class="inline"><span class="inline-delimiter">#{</span><span class="instance-variable">@name</span><span class="inline-delimiter">}</span></span><span class="content">!</span><span class="delimiter">"</span></span>
<span class="line-numbers"> 9</span> <span class="reserved">end</span>
<span class="line-numbers"><strong>10</strong></span> <span class="reserved">end</span></code>
</pre>
</body>
</html>

View File

@ -91,105 +91,97 @@ div.action_A { background: #bfb }
/************* CodeRay styles *************/
.syntaxhl div {display: inline;}
.syntaxhl .no { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 }
.syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin:0px 5px 0px 0px; }
.syntaxhl .code pre { overflow: auto }
.syntaxhl .debug { color:white ! important; background:blue ! important; }
.syntaxhl .af { color:#00C }
.syntaxhl .an { color:#007 }
.syntaxhl .at { color:#f08 }
.syntaxhl .av { color:#700 }
.syntaxhl .aw { color:#C00 }
.syntaxhl .bi { color:#509; font-weight:bold }
.syntaxhl .c { color:#888; }
.syntaxhl .attribute-name { color:#007 }
.syntaxhl .annotation { color:#f08 }
.syntaxhl .attribute-value { color:#700 }
.syntaxhl .bin { color:#509; font-weight:bold }
.syntaxhl .comment { color:#888; }
.syntaxhl .ch { color:#04D }
.syntaxhl .ch .k { color:#04D }
.syntaxhl .ch .dl { color:#039 }
.syntaxhl .char { color:#04D }
.syntaxhl .char .content { color:#04D }
.syntaxhl .char .delimiter { color:#039 }
.syntaxhl .cl { color:#B06; font-weight:bold }
.syntaxhl .cm { color:#A08; font-weight:bold }
.syntaxhl .co { color:#036; font-weight:bold }
.syntaxhl .cr { color:#0A0 }
.syntaxhl .cv { color:#369 }
.syntaxhl .de { color:#B0B; }
.syntaxhl .df { color:#099; font-weight:bold }
.syntaxhl .di { color:#088; font-weight:bold }
.syntaxhl .dl { color:black }
.syntaxhl .do { color:#970 }
.syntaxhl .dt { color:#34b }
.syntaxhl .ds { color:#D42; font-weight:bold }
.syntaxhl .e { color:#666; font-weight:bold }
.syntaxhl .en { color:#800; font-weight:bold }
.syntaxhl .er { color:#F00; background-color:#FAA }
.syntaxhl .ex { color:#C00; font-weight:bold }
.syntaxhl .fl { color:#60E; font-weight:bold }
.syntaxhl .fu { color:#06B; font-weight:bold }
.syntaxhl .gv { color:#d70; font-weight:bold }
.syntaxhl .hx { color:#058; font-weight:bold }
.syntaxhl .i { color:#00D; font-weight:bold }
.syntaxhl .ic { color:#B44; font-weight:bold }
.syntaxhl .class { color:#B06; font-weight:bold }
.syntaxhl .complex { color:#A08; font-weight:bold }
.syntaxhl .constant { color:#036; font-weight:bold }
.syntaxhl .color { color:#0A0 }
.syntaxhl .class-variable { color:#369 }
.syntaxhl .decorator { color:#B0B; }
.syntaxhl .definition { color:#099; font-weight:bold }
.syntaxhl .directive { color:#088; font-weight:bold }
.syntaxhl .delimiter { color:black }
.syntaxhl .doc { color:#970 }
.syntaxhl .doctype { color:#34b }
.syntaxhl .doc-string { color:#D42; font-weight:bold }
.syntaxhl .escape { color:#666; font-weight:bold }
.syntaxhl .entity { color:#800; font-weight:bold }
.syntaxhl .error { color:#F00; background-color:#FAA }
.syntaxhl .exception { color:#C00; font-weight:bold }
.syntaxhl .float { color:#60E; font-weight:bold }
.syntaxhl .function { color:#06B; font-weight:bold }
.syntaxhl .global-variable { color:#d70; font-weight:bold }
.syntaxhl .hex { color:#058; font-weight:bold }
.syntaxhl .integer { color:#00D; font-weight:bold }
.syntaxhl .include { color:#B44; font-weight:bold }
.syntaxhl .il { background: #ddd; color: black }
.syntaxhl .il .il { background: #ccc }
.syntaxhl .il .il .il { background: #bbb }
.syntaxhl .il .idl { background: #ddd; font-weight: bold; color: #666 }
.syntaxhl .idl { background-color: #bbb; font-weight: bold; color: #666; }
.syntaxhl .inline { background: #ddd; color: black }
.syntaxhl .inline .inline { background: #ccc }
.syntaxhl .inline .inline .inline { background: #bbb }
.syntaxhl .inline .inline-delimiter { background: #ddd; font-weight: bold; color: #666 }
.syntaxhl .inline-delimiter { background-color: #bbb; font-weight: bold; color: #666; }
.syntaxhl .im { color:#f00; }
.syntaxhl .in { color:#B2B; font-weight:bold }
.syntaxhl .iv { color:#33B }
.syntaxhl .la { color:#970; font-weight:bold }
.syntaxhl .lv { color:#963 }
.syntaxhl .oc { color:#40E; font-weight:bold }
.syntaxhl .of { color:#000; font-weight:bold }
.syntaxhl .op { }
.syntaxhl .pc { color:#038; font-weight:bold }
.syntaxhl .pd { color:#369; font-weight:bold }
.syntaxhl .pp { color:#579; }
.syntaxhl .ps { color:#00C; font-weight:bold }
.syntaxhl .pt { color:#074; font-weight:bold }
.syntaxhl .r, .kw { color:#080; font-weight:bold }
.syntaxhl .important { color:#f00; }
.syntaxhl .instance-variable { color:#33B }
.syntaxhl .label { color:#970; font-weight:bold }
.syntaxhl .local-variable { color:#963 }
.syntaxhl .octal { color:#40E; font-weight:bold }
.syntaxhl .predefined-constant { color:#038; font-weight:bold }
.syntaxhl .predefined { color:#369; font-weight:bold }
.syntaxhl .preprocessor { color:#579; }
.syntaxhl .pseudo-class { color:#00C; font-weight:bold }
.syntaxhl .predefined-type { color:#074; font-weight:bold }
.syntaxhl .reserved, .keyword { color:#080; font-weight:bold }
.syntaxhl .ke { color: #808; }
.syntaxhl .ke .dl { color: #606; }
.syntaxhl .ke .ch { color: #80f; }
.syntaxhl .vl { color: #088; }
.syntaxhl .key { color: #808; }
.syntaxhl .key .delimiter { color: #606; }
.syntaxhl .key .char { color: #80f; }
.syntaxhl .value { color: #088; }
.syntaxhl .rx { background-color:#fff0ff }
.syntaxhl .rx .k { color:#808 }
.syntaxhl .rx .dl { color:#404 }
.syntaxhl .rx .mod { color:#C2C }
.syntaxhl .rx .fu { color:#404; font-weight: bold }
.syntaxhl .regexp { background-color:#fff0ff }
.syntaxhl .regexp .content { color:#808 }
.syntaxhl .regexp .delimiter { color:#404 }
.syntaxhl .regexp .modifier { color:#C2C }
.syntaxhl .regexp .function { color:#404; font-weight: bold }
.syntaxhl .s { background-color:#fff0f0; color: #D20; }
.syntaxhl .s .s { background-color:#ffe0e0 }
.syntaxhl .s .s .s { background-color:#ffd0d0 }
.syntaxhl .s .k { }
.syntaxhl .s .ch { color: #b0b; }
.syntaxhl .s .dl { color: #710; }
.syntaxhl .string { background-color:#fff0f0; color: #D20; }
.syntaxhl .string .string { background-color:#ffe0e0 }
.syntaxhl .string .string .string { background-color:#ffd0d0 }
.syntaxhl .string .content { }
.syntaxhl .string .char { color: #b0b; }
.syntaxhl .string .delimiter { color: #710; }
.syntaxhl .sh { background-color:#f0fff0; color:#2B2 }
.syntaxhl .sh .k { }
.syntaxhl .sh .dl { color:#161 }
.syntaxhl .shell { background-color:#f0fff0; color:#2B2 }
.syntaxhl .shell .content { }
.syntaxhl .shell .delimiter { color:#161 }
.syntaxhl .sy { color:#A60 }
.syntaxhl .sy .k { color:#A60 }
.syntaxhl .sy .dl { color:#630 }
.syntaxhl .symbol { color:#A60 }
.syntaxhl .symbol .content { color:#A60 }
.syntaxhl .symbol .delimiter { color:#630 }
.syntaxhl .ta { color:#070 }
.syntaxhl .tf { color:#070; font-weight:bold }
.syntaxhl .ts { color:#D70; font-weight:bold }
.syntaxhl .ty { color:#339; font-weight:bold }
.syntaxhl .v { color:#036 }
.syntaxhl .xt { color:#444 }
.syntaxhl .tag { color:#070 }
.syntaxhl .type { color:#339; font-weight:bold }
.syntaxhl .variable { color:#036 }
.syntaxhl .ins { background: #cfc; }
.syntaxhl .del { background: #fcc; }
.syntaxhl .chg { color: #aaf; background: #007; }
.syntaxhl .insert { background: #cfc; }
.syntaxhl .delete { background: #fcc; }
.syntaxhl .change { color: #aaf; background: #007; }
.syntaxhl .head { color: #f8f; background: #505 }
.syntaxhl .ins .ins { color: #080; font-weight:bold }
.syntaxhl .del .del { color: #800; font-weight:bold }
.syntaxhl .chg .chg { color: #66f; }
.syntaxhl .insert .insert { color: #080; font-weight:bold }
.syntaxhl .delete .delete { color: #800; font-weight:bold }
.syntaxhl .change .change { color: #66f; }
.syntaxhl .head .head { color: #f4f; }

View File

@ -22,7 +22,7 @@ Index: app/views/common/_diff.rhtml
+<% diff.each do |table_file| -%>
<div class="autoscroll">
<% if diff_type == 'sbs' -%>
<table class="filecontent CodeRay">
<table class="filecontent syntaxhl">
@@ -62,3 +63,5 @@
</div>

View File

@ -499,7 +499,7 @@ EXPECTED
RAW
expected = <<-EXPECTED
<pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="no">1</span> <span class="c"># Some ruby code here</span></span>
<pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="line-numbers">1</span> <span class="comment"># Some ruby code here</span></span>
</code></pre>
EXPECTED

View File

@ -1,155 +0,0 @@
--- !ruby/object:Gem::Specification
name: coderay
version: !ruby/object:Gem::Version
hash: 53
prerelease: false
segments:
- 0
- 9
- 7
version: 0.9.7
platform: ruby
authors:
- murphy
autorequire:
bindir: bin
cert_chain: []
date: 2011-01-15 00:00:00 +01:00
default_executable:
dependencies: []
description: |
Fast and easy syntax highlighting for selected languages, written in Ruby.
Comes with RedCloth integration and LOC counter.
email: murphy@rubychan.de
executables:
- coderay
- coderay_stylesheet
extensions: []
extra_rdoc_files:
- lib/README
- FOLDERS
files:
- ./lib/coderay/duo.rb
- ./lib/coderay/encoder.rb
- ./lib/coderay/encoders/_map.rb
- ./lib/coderay/encoders/comment_filter.rb
- ./lib/coderay/encoders/count.rb
- ./lib/coderay/encoders/debug.rb
- ./lib/coderay/encoders/div.rb
- ./lib/coderay/encoders/filter.rb
- ./lib/coderay/encoders/html/css.rb
- ./lib/coderay/encoders/html/numerization.rb
- ./lib/coderay/encoders/html/output.rb
- ./lib/coderay/encoders/html.rb
- ./lib/coderay/encoders/json.rb
- ./lib/coderay/encoders/lines_of_code.rb
- ./lib/coderay/encoders/null.rb
- ./lib/coderay/encoders/page.rb
- ./lib/coderay/encoders/span.rb
- ./lib/coderay/encoders/statistic.rb
- ./lib/coderay/encoders/term.rb
- ./lib/coderay/encoders/text.rb
- ./lib/coderay/encoders/token_class_filter.rb
- ./lib/coderay/encoders/xml.rb
- ./lib/coderay/encoders/yaml.rb
- ./lib/coderay/for_redcloth.rb
- ./lib/coderay/helpers/file_type.rb
- ./lib/coderay/helpers/gzip_simple.rb
- ./lib/coderay/helpers/plugin.rb
- ./lib/coderay/helpers/word_list.rb
- ./lib/coderay/scanner.rb
- ./lib/coderay/scanners/_map.rb
- ./lib/coderay/scanners/c.rb
- ./lib/coderay/scanners/cpp.rb
- ./lib/coderay/scanners/css.rb
- ./lib/coderay/scanners/debug.rb
- ./lib/coderay/scanners/delphi.rb
- ./lib/coderay/scanners/diff.rb
- ./lib/coderay/scanners/groovy.rb
- ./lib/coderay/scanners/html.rb
- ./lib/coderay/scanners/java/builtin_types.rb
- ./lib/coderay/scanners/java.rb
- ./lib/coderay/scanners/java_script-0.9.6.rb
- ./lib/coderay/scanners/java_script.rb
- ./lib/coderay/scanners/json.rb
- ./lib/coderay/scanners/nitro_xhtml.rb
- ./lib/coderay/scanners/php.rb
- ./lib/coderay/scanners/plaintext.rb
- ./lib/coderay/scanners/python.rb
- ./lib/coderay/scanners/rhtml.rb
- ./lib/coderay/scanners/ruby/patterns.rb
- ./lib/coderay/scanners/ruby.rb
- ./lib/coderay/scanners/scheme.rb
- ./lib/coderay/scanners/sql.rb
- ./lib/coderay/scanners/xml.rb
- ./lib/coderay/scanners/yaml.rb
- ./lib/coderay/style.rb
- ./lib/coderay/styles/_map.rb
- ./lib/coderay/styles/cycnus.rb
- ./lib/coderay/styles/murphy.rb
- ./lib/coderay/token_classes.rb
- ./lib/coderay/tokens.rb
- ./lib/coderay.rb
- ./Rakefile
- ./test/functional/basic.rb
- ./test/functional/basic.rbc
- ./test/functional/for_redcloth.rb
- ./test/functional/for_redcloth.rbc
- ./test/functional/load_plugin_scanner.rb
- ./test/functional/load_plugin_scanner.rbc
- ./test/functional/suite.rb
- ./test/functional/suite.rbc
- ./test/functional/vhdl.rb
- ./test/functional/vhdl.rbc
- ./test/functional/word_list.rb
- ./test/functional/word_list.rbc
- ./lib/README
- ./LICENSE
- lib/README
- FOLDERS
- bin/coderay
- bin/coderay_stylesheet
has_rdoc: true
homepage: http://coderay.rubychan.de
licenses: []
post_install_message:
rdoc_options:
- -SNw2
- -mlib/README
- -t CodeRay Documentation
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 51
segments:
- 1
- 8
- 2
version: 1.8.2
required_rubygems_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
requirements: []
rubyforge_project: coderay
rubygems_version: 1.3.7
signing_key:
specification_version: 3
summary: Fast syntax highlighting for selected languages.
test_files:
- ./test/functional/suite.rb

View File

@ -1,53 +0,0 @@
= CodeRay - Trunk folder structure
== bench - Benchmarking system
All benchmarking stuff goes here.
Test inputs are stored in files named <code>example.<lang></code>.
Test outputs go to <code>bench/test.<encoder-default-file-extension></code>.
Run <code>bench/bench.rb</code> to get a usage description.
Run <code>rake bench</code> to perform an example benchmark.
== bin - Scripts
Executional files for CodeRay.
== demo - Demos and functional tests
Demonstrational scripts to show of CodeRay's features.
Run them as functional tests with <code>rake test:demos</code>.
== etc - Lots of stuff
Some addidtional files for CodeRay, mainly graphics and Vim scripts.
== gem_server - Gem output folder
For <code>rake gem</code>.
== lib - CodeRay library code
This is the base directory for the CodeRay library.
== rake_helpers - Rake helper libraries
Some files to enhance Rake, including the Autumnal Rdoc template and some scripts.
== test - Tests
Tests for the scanners.
Each language has its own subfolder and sub-suite.
Run with <code>rake test</code>.

View File

@ -1,86 +0,0 @@
#!/usr/bin/env ruby
# CodeRay Executable
#
# Version: 0.2
# Author: murphy
require 'coderay'
if ARGV.empty?
$stderr.puts <<-USAGE
CodeRay #{CodeRay::VERSION} (http://coderay.rubychan.de)
Usage:
coderay file [-<format>]
coderay -<lang> [-<format>] [< file] [> output]
Defaults:
lang: based on file extension
format: ANSI colorized output for terminal, HTML page for files
Examples:
coderay foo.rb # colorized output to terminal, based on file extension
coderay foo.rb -loc # print LOC count, based on file extension and format
coderay foo.rb > foo.html # HTML page output to file, based on extension
coderay -ruby < foo.rb # colorized output to terminal, based on lang
coderay -ruby -loc < foo.rb # print LOC count, based on lang
coderay -ruby -page foo.rb # HTML page output to terminal, based on lang and format
coderay -ruby -page foo.rb > foo.html # HTML page output to file, based on lang and format
USAGE
end
first, second = ARGV
def read
file = ARGV.grep(/^(?!-)/).last
if file
if File.exist?(file)
File.read file
else
$stderr.puts "No such file: #{file}"
end
else
$stdin.read
end
end
if first
if first[/-(\w+)/] == first
lang = $1
input = read
tokens = :scan
else
file = first
unless File.exist? file
$stderr.puts "No such file: #{file}"
exit 2
end
tokens = CodeRay.scan_file file
end
else
$stderr.puts 'No lang/file given.'
exit 1
end
if second
if second[/-(\w+)/] == second
format = $1.to_sym
else
raise 'invalid format (must be -xxx)'
end
else
if $stdout.tty?
format = :term
else
$stderr.puts 'No format given; setting to default (HTML Page).'
format = :page
end
end
if tokens == :scan
output = CodeRay::Duo[lang => format].highlight input
else
output = tokens.encode format
end
out = $stdout
out.puts output

View File

@ -1,4 +0,0 @@
#!/usr/bin/env ruby
require 'coderay'
puts CodeRay::Encoders[:html]::CSS.new.stylesheet

View File

@ -1,322 +0,0 @@
# = CodeRay Library
#
# CodeRay is a Ruby library for syntax highlighting.
#
# I try to make CodeRay easy to use and intuitive, but at the same time fully featured, complete,
# fast and efficient.
#
# See README.
#
# It consists mainly of
# * the main engine: CodeRay (Scanners::Scanner, Tokens/TokenStream, Encoders::Encoder), PluginHost
# * the scanners in CodeRay::Scanners
# * the encoders in CodeRay::Encoders
#
# Here's a fancy graphic to light up this gray docu:
#
# http://cycnus.de/raindark/coderay/scheme.png
#
# == Documentation
#
# See CodeRay, Encoders, Scanners, Tokens.
#
# == Usage
#
# Remember you need RubyGems to use CodeRay, unless you have it in your load path. Run Ruby with
# -rubygems option if required.
#
# === Highlight Ruby code in a string as html
#
# require 'coderay'
# print CodeRay.scan('puts "Hello, world!"', :ruby).html
#
# # prints something like this:
# puts <span class="s">&quot;Hello, world!&quot;</span>
#
#
# === Highlight C code from a file in a html div
#
# require 'coderay'
# print CodeRay.scan(File.read('ruby.h'), :c).div
# print CodeRay.scan_file('ruby.h').html.div
#
# You can include this div in your page. The used CSS styles can be printed with
#
# % coderay_stylesheet
#
# === Highlight without typing too much
#
# If you are one of the hasty (or lazy, or extremely curious) people, just run this file:
#
# % ruby -rubygems /path/to/coderay/coderay.rb > example.html
#
# and look at the file it created in your browser.
#
# = CodeRay Module
#
# The CodeRay module provides convenience methods for the engine.
#
# * The +lang+ and +format+ arguments select Scanner and Encoder to use. These are
# simply lower-case symbols, like <tt>:python</tt> or <tt>:html</tt>.
# * All methods take an optional hash as last parameter, +options+, that is send to
# the Encoder / Scanner.
# * Input and language are always sorted in this order: +code+, +lang+.
# (This is in alphabetical order, if you need a mnemonic ;)
#
# You should be able to highlight everything you want just using these methods;
# so there is no need to dive into CodeRay's deep class hierarchy.
#
# The examples in the demo directory demonstrate common cases using this interface.
#
# = Basic Access Ways
#
# Read this to get a general view what CodeRay provides.
#
# == Scanning
#
# Scanning means analysing an input string, splitting it up into Tokens.
# Each Token knows about what type it is: string, comment, class name, etc.
#
# Each +lang+ (language) has its own Scanner; for example, <tt>:ruby</tt> code is
# handled by CodeRay::Scanners::Ruby.
#
# CodeRay.scan:: Scan a string in a given language into Tokens.
# This is the most common method to use.
# CodeRay.scan_file:: Scan a file and guess the language using FileType.
#
# The Tokens object you get from these methods can encode itself; see Tokens.
#
# == Encoding
#
# Encoding means compiling Tokens into an output. This can be colored HTML or
# LaTeX, a textual statistic or just the number of non-whitespace tokens.
#
# Each Encoder provides output in a specific +format+, so you select Encoders via
# formats like <tt>:html</tt> or <tt>:statistic</tt>.
#
# CodeRay.encode:: Scan and encode a string in a given language.
# CodeRay.encode_tokens:: Encode the given tokens.
# CodeRay.encode_file:: Scan a file, guess the language using FileType and encode it.
#
# == Streaming
#
# Streaming saves RAM by running Scanner and Encoder in some sort of
# pipe mode; see TokenStream.
#
# CodeRay.scan_stream:: Scan in stream mode.
#
# == All-in-One Encoding
#
# CodeRay.encode:: Highlight a string with a given input and output format.
#
# == Instanciating
#
# You can use an Encoder instance to highlight multiple inputs. This way, the setup
# for this Encoder must only be done once.
#
# CodeRay.encoder:: Create an Encoder instance with format and options.
# CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code.
#
# To make use of CodeRay.scanner, use CodeRay::Scanner::code=.
#
# The scanning methods provide more flexibility; we recommend to use these.
#
# == Reusing Scanners and Encoders
#
# If you want to re-use scanners and encoders (because that is faster), see
# CodeRay::Duo for the most convenient (and recommended) interface.
module CodeRay
$CODERAY_DEBUG ||= false
# Version: Major.Minor.Teeny[.Revision]
# Major: 0 for pre-stable, 1 for stable
# Minor: feature milestone
# Teeny: development state, 0 for pre-release
# Revision: Subversion Revision number (generated on rake gem:make)
VERSION = '0.9.7'
require 'coderay/tokens'
require 'coderay/token_classes'
require 'coderay/scanner'
require 'coderay/encoder'
require 'coderay/duo'
require 'coderay/style'
class << self
# Scans the given +code+ (a String) with the Scanner for +lang+.
#
# This is a simple way to use CodeRay. Example:
# require 'coderay'
# page = CodeRay.scan("puts 'Hello, world!'", :ruby).html
#
# See also demo/demo_simple.
def scan code, lang, options = {}, &block
scanner = Scanners[lang].new code, options, &block
scanner.tokenize
end
# Scans +filename+ (a path to a code file) with the Scanner for +lang+.
#
# If +lang+ is :auto or omitted, the CodeRay::FileType module is used to
# determine it. If it cannot find out what type it is, it uses
# CodeRay::Scanners::Plaintext.
#
# Calls CodeRay.scan.
#
# Example:
# require 'coderay'
# page = CodeRay.scan_file('some_c_code.c').html
def scan_file filename, lang = :auto, options = {}, &block
file = IO.read filename
if lang == :auto
require 'coderay/helpers/file_type'
lang = FileType.fetch filename, :plaintext, true
end
scan file, lang, options = {}, &block
end
# Scan the +code+ (a string) with the scanner for +lang+.
#
# Calls scan.
#
# See CodeRay.scan.
def scan_stream code, lang, options = {}, &block
options[:stream] = true
scan code, lang, options, &block
end
# Encode a string in Streaming mode.
#
# This starts scanning +code+ with the the Scanner for +lang+
# while encodes the output with the Encoder for +format+.
# +options+ will be passed to the Encoder.
#
# See CodeRay::Encoder.encode_stream
def encode_stream code, lang, format, options = {}
encoder(format, options).encode_stream code, lang, options
end
# Encode a string.
#
# This scans +code+ with the the Scanner for +lang+ and then
# encodes it with the Encoder for +format+.
# +options+ will be passed to the Encoder.
#
# See CodeRay::Encoder.encode
def encode code, lang, format, options = {}
encoder(format, options).encode code, lang, options
end
# Highlight a string into a HTML <div>.
#
# CSS styles use classes, so you have to include a stylesheet
# in your output.
#
# See encode.
def highlight code, lang, options = { :css => :class }, format = :div
encode code, lang, format, options
end
# Encode pre-scanned Tokens.
# Use this together with CodeRay.scan:
#
# require 'coderay'
#
# # Highlight a short Ruby code example in a HTML span
# tokens = CodeRay.scan '1 + 2', :ruby
# puts CodeRay.encode_tokens(tokens, :span)
#
def encode_tokens tokens, format, options = {}
encoder(format, options).encode_tokens tokens, options
end
# Encodes +filename+ (a path to a code file) with the Scanner for +lang+.
#
# See CodeRay.scan_file.
# Notice that the second argument is the output +format+, not the input language.
#
# Example:
# require 'coderay'
# page = CodeRay.encode_file 'some_c_code.c', :html
def encode_file filename, format, options = {}
tokens = scan_file filename, :auto, get_scanner_options(options)
encode_tokens tokens, format, options
end
# Highlight a file into a HTML <div>.
#
# CSS styles use classes, so you have to include a stylesheet
# in your output.
#
# See encode.
def highlight_file filename, options = { :css => :class }, format = :div
encode_file filename, format, options
end
# Finds the Encoder class for +format+ and creates an instance, passing
# +options+ to it.
#
# Example:
# require 'coderay'
#
# stats = CodeRay.encoder(:statistic)
# stats.encode("puts 17 + 4\n", :ruby)
#
# puts '%d out of %d tokens have the kind :integer.' % [
# stats.type_stats[:integer].count,
# stats.real_token_count
# ]
# #-> 2 out of 4 tokens have the kind :integer.
def encoder format, options = {}
Encoders[format].new options
end
# Finds the Scanner class for +lang+ and creates an instance, passing
# +options+ to it.
#
# See Scanner.new.
def scanner lang, options = {}
Scanners[lang].new '', options
end
# Extract the options for the scanner from the +options+ hash.
#
# Returns an empty Hash if <tt>:scanner_options</tt> is not set.
#
# This is used if a method like CodeRay.encode has to provide options
# for Encoder _and_ scanner.
def get_scanner_options options
options.fetch :scanner_options, {}
end
end
# This Exception is raised when you try to stream with something that is not
# capable of streaming.
class NotStreamableError < Exception
def initialize obj
@obj = obj
end
def to_s
'%s is not Streamable!' % @obj.class
end
end
# A dummy module that is included by subclasses of CodeRay::Scanner an CodeRay::Encoder
# to show that they are able to handle streams.
module Streamable
end
end
# Run a test script.
if $0 == __FILE__
$stderr.print 'Press key to print demo.'; gets
# Just use this file as an example of Ruby code.
code = File.read(__FILE__)[/module CodeRay.*/m]
print CodeRay.scan(code, :ruby).html
end

View File

@ -1,12 +0,0 @@
module CodeRay
module Encoders
map \
:loc => :lines_of_code,
:plain => :text,
:stats => :statistic,
:terminal => :term,
:tex => :latex
end
end

View File

@ -1,43 +0,0 @@
($:.unshift '../..'; require 'coderay') unless defined? CodeRay
module CodeRay
module Encoders
load :token_class_filter
class CommentFilter < TokenClassFilter
register_for :comment_filter
DEFAULT_OPTIONS = superclass::DEFAULT_OPTIONS.merge \
:exclude => [:comment]
end
end
end
if $0 == __FILE__
$VERBOSE = true
$: << File.join(File.dirname(__FILE__), '..')
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
class CommentFilterTest < Test::Unit::TestCase
def test_filtering_comments
tokens = CodeRay.scan <<-RUBY, :ruby
#!/usr/bin/env ruby
# a minimal Ruby program
puts "Hello world!"
RUBY
assert_equal <<-RUBY_FILTERED, tokens.comment_filter.text
#!/usr/bin/env ruby
puts "Hello world!"
RUBY_FILTERED
end
end

View File

@ -1,21 +0,0 @@
module CodeRay
module Encoders
class Count < Encoder
include Streamable
register_for :count
protected
def setup options
@out = 0
end
def token text, kind
@out += 1
end
end
end
end

View File

@ -1,19 +0,0 @@
module CodeRay
module Encoders
load :html
class Div < HTML
FILE_EXTENSION = 'div.html'
register_for :div
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \
:css => :style,
:wrap => :div
end
end
end

View File

@ -1,75 +0,0 @@
($:.unshift '../..'; require 'coderay') unless defined? CodeRay
module CodeRay
module Encoders
class Filter < Encoder
register_for :filter
protected
def setup options
@out = Tokens.new
end
def text_token text, kind
[text, kind] if include_text_token? text, kind
end
def include_text_token? text, kind
true
end
def block_token action, kind
[action, kind] if include_block_token? action, kind
end
def include_block_token? action, kind
true
end
end
end
end
if $0 == __FILE__
$VERBOSE = true
$: << File.join(File.dirname(__FILE__), '..')
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
class FilterTest < Test::Unit::TestCase
def test_creation
assert CodeRay::Encoders::Filter < CodeRay::Encoders::Encoder
filter = nil
assert_nothing_raised do
filter = CodeRay.encoder :filter
end
assert_kind_of CodeRay::Encoders::Encoder, filter
end
def test_filtering_text_tokens
tokens = CodeRay::Tokens.new
10.times do |i|
tokens << [i.to_s, :index]
end
assert_equal tokens, CodeRay::Encoders::Filter.new.encode_tokens(tokens)
assert_equal tokens, tokens.filter
end
def test_filtering_block_tokens
tokens = CodeRay::Tokens.new
10.times do |i|
tokens << [:open, :index]
tokens << [i.to_s, :content]
tokens << [:close, :index]
end
assert_equal tokens, CodeRay::Encoders::Filter.new.encode_tokens(tokens)
assert_equal tokens, tokens.filter
end
end

View File

@ -1,309 +0,0 @@
require 'set'
module CodeRay
module Encoders
# = HTML Encoder
#
# This is CodeRay's most important highlighter:
# It provides save, fast XHTML generation and CSS support.
#
# == Usage
#
# require 'coderay'
# puts CodeRay.scan('Some /code/', :ruby).html #-> a HTML page
# puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span)
# #-> <span class="CodeRay"><span class="co">Some</span> /code/</span>
# puts CodeRay.scan('Some /code/', :ruby).span #-> the same
#
# puts CodeRay.scan('Some code', :ruby).html(
# :wrap => nil,
# :line_numbers => :inline,
# :css => :style
# )
# #-> <span class="no">1</span> <span style="color:#036; font-weight:bold;">Some</span> code
#
# == Options
#
# === :tab_width
# Convert \t characters to +n+ spaces (a number.)
# Default: 8
#
# === :css
# How to include the styles; can be :class or :style.
#
# Default: :class
#
# === :wrap
# Wrap in :page, :div, :span or nil.
#
# You can also use Encoders::Div and Encoders::Span.
#
# Default: nil
#
# === :title
#
# The title of the HTML page (works only when :wrap is set to :page.)
#
# Default: 'CodeRay output'
#
# === :line_numbers
# Include line numbers in :table, :inline, :list or nil (no line numbers)
#
# Default: nil
#
# === :line_number_start
# Where to start with line number counting.
#
# Default: 1
#
# === :bold_every
# Make every +n+-th number appear bold.
#
# Default: 10
#
# === :highlight_lines
#
# Highlights certain line numbers.
# Can be any Enumerable, typically just an Array or Range, of numbers.
#
# Bolding is deactivated when :highlight_lines is set. It only makes sense
# in combination with :line_numbers.
#
# Default: nil
#
# === :hint
# Include some information into the output using the title attribute.
# Can be :info (show token type on mouse-over), :info_long (with full path)
# or :debug (via inspect).
#
# Default: false
class HTML < Encoder
include Streamable
register_for :html
FILE_EXTENSION = 'html'
DEFAULT_OPTIONS = {
:tab_width => 8,
:css => :class,
:style => :cycnus,
:wrap => nil,
:title => 'CodeRay output',
:line_numbers => nil,
:line_number_start => 1,
:bold_every => 10,
:highlight_lines => nil,
:hint => false,
}
helper :output, :css
attr_reader :css
protected
HTML_ESCAPE = { #:nodoc:
'&' => '&amp;',
'"' => '&quot;',
'>' => '&gt;',
'<' => '&lt;',
}
# This was to prevent illegal HTML.
# Strange chars should still be avoided in codes.
evil_chars = Array(0x00...0x20) - [?\n, ?\t, ?\s]
evil_chars.each { |i| HTML_ESCAPE[i.chr] = ' ' }
#ansi_chars = Array(0x7f..0xff)
#ansi_chars.each { |i| HTML_ESCAPE[i.chr] = '&#%d;' % i }
# \x9 (\t) and \xA (\n) not included
#HTML_ESCAPE_PATTERN = /[\t&"><\0-\x8\xB-\x1f\x7f-\xff]/
HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1f]/
TOKEN_KIND_TO_INFO = Hash.new { |h, kind|
h[kind] =
case kind
when :pre_constant
'Predefined constant'
else
kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize }
end
}
TRANSPARENT_TOKEN_KINDS = [
:delimiter, :modifier, :content, :escape, :inline_delimiter,
].to_set
# Generate a hint about the given +classes+ in a +hint+ style.
#
# +hint+ may be :info, :info_long or :debug.
def self.token_path_to_hint hint, classes
title =
case hint
when :info
TOKEN_KIND_TO_INFO[classes.first]
when :info_long
classes.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/')
when :debug
classes.inspect
end
title ? " title=\"#{title}\"" : ''
end
def setup options
super
@HTML_ESCAPE = HTML_ESCAPE.dup
@HTML_ESCAPE["\t"] = ' ' * options[:tab_width]
@opened = [nil]
@css = CSS.new options[:style]
hint = options[:hint]
if hint and not [:debug, :info, :info_long].include? hint
raise ArgumentError, "Unknown value %p for :hint; \
expected :info, :debug, false, or nil." % hint
end
case options[:css]
when :class
@css_style = Hash.new do |h, k|
c = CodeRay::Tokens::ClassOfKind[k.first]
if c == :NO_HIGHLIGHT and not hint
h[k.dup] = false
else
title = if hint
HTML.token_path_to_hint(hint, k[1..-1] << k.first)
else
''
end
if c == :NO_HIGHLIGHT
h[k.dup] = '<span%s>' % [title]
else
h[k.dup] = '<span%s class="%s">' % [title, c]
end
end
end
when :style
@css_style = Hash.new do |h, k|
if k.is_a? ::Array
styles = k.dup
else
styles = [k]
end
type = styles.first
classes = styles.map { |c| Tokens::ClassOfKind[c] }
if classes.first == :NO_HIGHLIGHT and not hint
h[k] = false
else
styles.shift if TRANSPARENT_TOKEN_KINDS.include? styles.first
title = HTML.token_path_to_hint hint, styles
style = @css[*classes]
h[k] =
if style
'<span%s style="%s">' % [title, style]
else
false
end
end
end
else
raise ArgumentError, "Unknown value %p for :css." % options[:css]
end
end
def finish options
not_needed = @opened.shift
@out << '</span>' * @opened.size
unless @opened.empty?
warn '%d tokens still open: %p' % [@opened.size, @opened]
end
@out.extend Output
@out.css = @css
@out.numerize! options[:line_numbers], options
@out.wrap! options[:wrap]
@out.apply_title! options[:title]
super
end
def token text, type = :plain
case text
when nil
# raise 'Token with nil as text was given: %p' % [[text, type]]
when String
if text =~ /#{HTML_ESCAPE_PATTERN}/o
text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] }
end
@opened[0] = type
if text != "\n" && style = @css_style[@opened]
@out << style << text << '</span>'
else
@out << text
end
# token groups, eg. strings
when :open
@opened[0] = type
@out << (@css_style[@opened] || '<span>')
@opened << type
when :close
if @opened.empty?
# nothing to close
else
if $CODERAY_DEBUG and (@opened.size == 1 or @opened.last != type)
raise 'Malformed token stream: Trying to close a token (%p) \
that is not open. Open are: %p.' % [type, @opened[1..-1]]
end
@out << '</span>'
@opened.pop
end
# whole lines to be highlighted, eg. a deleted line in a diff
when :begin_line
@opened[0] = type
if style = @css_style[@opened]
if style['class="']
@out << style.sub('class="', 'class="line ')
else
@out << style.sub('>', ' class="line">')
end
else
@out << '<span class="line">'
end
@opened << type
when :end_line
if @opened.empty?
# nothing to close
else
if $CODERAY_DEBUG and (@opened.size == 1 or @opened.last != type)
raise 'Malformed token stream: Trying to close a line (%p) \
that is not open. Open are: %p.' % [type, @opened[1..-1]]
end
@out << '</span>'
@opened.pop
end
else
raise 'unknown token kind: %p' % [text]
end
end
end
end
end

View File

@ -1,70 +0,0 @@
module CodeRay
module Encoders
class HTML
class CSS
attr :stylesheet
def CSS.load_stylesheet style = nil
CodeRay::Styles[style]
end
def initialize style = :default
@classes = Hash.new
style = CSS.load_stylesheet style
@stylesheet = [
style::CSS_MAIN_STYLES,
style::TOKEN_COLORS.gsub(/^(?!$)/, '.CodeRay ')
].join("\n")
parse style::TOKEN_COLORS
end
def [] *styles
cl = @classes[styles.first]
return '' unless cl
style = ''
1.upto(styles.size) do |offset|
break if style = cl[styles[offset .. -1]]
end
# warn 'Style not found: %p' % [styles] if style.empty?
return style
end
private
CSS_CLASS_PATTERN = /
( # $1 = selectors
(?:
(?: \s* \. [-\w]+ )+
\s* ,?
)+
)
\s* \{ \s*
( [^\}]+ )? # $2 = style
\s* \} \s*
|
( . ) # $3 = error
/mx
def parse stylesheet
stylesheet.scan CSS_CLASS_PATTERN do |selectors, style, error|
raise "CSS parse error: '#{error.inspect}' not recognized" if error
for selector in selectors.split(',')
classes = selector.scan(/[-\w]+/)
cl = classes.pop
@classes[cl] ||= Hash.new
@classes[cl][classes] = style.to_s.strip.delete(' ').chomp(';')
end
end
end
end
end
end
end
if $0 == __FILE__
require 'pp'
pp CodeRay::Encoders::HTML::CSS.new
end

View File

@ -1,133 +0,0 @@
module CodeRay
module Encoders
class HTML
module Output
def numerize *args
clone.numerize!(*args)
end
=begin NUMERIZABLE_WRAPPINGS = {
:table => [:div, :page, nil],
:inline => :all,
:list => [:div, :page, nil]
}
NUMERIZABLE_WRAPPINGS.default = :all
=end
def numerize! mode = :table, options = {}
return self unless mode
options = DEFAULT_OPTIONS.merge options
start = options[:line_number_start]
unless start.is_a? Integer
raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start
end
#allowed_wrappings = NUMERIZABLE_WRAPPINGS[mode]
#unless allowed_wrappings == :all or allowed_wrappings.include? options[:wrap]
# raise ArgumentError, "Can't numerize, :wrap must be in %p, but is %p" % [NUMERIZABLE_WRAPPINGS, options[:wrap]]
#end
bold_every = options[:bold_every]
highlight_lines = options[:highlight_lines]
bolding =
if bold_every == false && highlight_lines == nil
proc { |line| line.to_s }
elsif highlight_lines.is_a? Enumerable
highlight_lines = highlight_lines.to_set
proc do |line|
if highlight_lines.include? line
"<strong class=\"highlighted\">#{line}</strong>" # highlighted line numbers in bold
else
line.to_s
end
end
elsif bold_every.is_a? Integer
raise ArgumentError, ":bolding can't be 0." if bold_every == 0
proc do |line|
if line % bold_every == 0
"<strong>#{line}</strong>" # every bold_every-th number in bold
else
line.to_s
end
end
else
raise ArgumentError, 'Invalid value %p for :bolding; false or Integer expected.' % bold_every
end
case mode
when :inline
max_width = (start + line_count).to_s.size
line_number = start
gsub!(/^/) do
line_number_text = bolding.call line_number
indent = ' ' * (max_width - line_number.to_s.size) # TODO: Optimize (10^x)
res = "<span class=\"no\">#{indent}#{line_number_text}</span> "
line_number += 1
res
end
when :table
# This is really ugly.
# Because even monospace fonts seem to have different heights when bold,
# I make the newline bold, both in the code and the line numbers.
# FIXME Still not working perfect for Mr. Internet Exploder
line_numbers = (start ... start + line_count).to_a.map(&bolding).join("\n")
line_numbers << "\n" # also for Mr. MS Internet Exploder :-/
line_numbers.gsub!(/\n/) { "<tt>\n</tt>" }
line_numbers_table_tpl = TABLE.apply('LINE_NUMBERS', line_numbers)
gsub!("</div>\n", '</div>')
gsub!("\n", "<tt>\n</tt>")
wrap_in! line_numbers_table_tpl
@wrapped_in = :div
when :list
opened_tags = []
gsub!(/^.*$\n?/) do |line|
line.chomp!
open = opened_tags.join
line.scan(%r!<(/)?span[^>]*>?!) do |close,|
if close
opened_tags.pop
else
opened_tags << $&
end
end
close = '</span>' * opened_tags.size
"<li>#{open}#{line}#{close}</li>\n"
end
chomp!("\n")
wrap_in! LIST
@wrapped_in = :div
else
raise ArgumentError, 'Unknown value %p for mode: expected one of %p' %
[mode, [:table, :list, :inline]]
end
self
end
def line_count
line_count = count("\n")
position_of_last_newline = rindex(?\n)
if position_of_last_newline
after_last_newline = self[position_of_last_newline + 1 .. -1]
ends_with_newline = after_last_newline[/\A(?:<\/span>)*\z/]
line_count += 1 if not ends_with_newline
end
line_count
end
end
end
end
end

View File

@ -1,206 +0,0 @@
module CodeRay
module Encoders
class HTML
# This module is included in the output String from thew HTML Encoder.
#
# It provides methods like wrap, div, page etc.
#
# Remember to use #clone instead of #dup to keep the modules the object was
# extended with.
#
# TODO: more doc.
module Output
require 'coderay/encoders/html/numerization.rb'
attr_accessor :css
class << self
# This makes Output look like a class.
#
# Example:
#
# a = Output.new '<span class="co">Code</span>'
# a.wrap! :page
def new string, css = CSS.new, element = nil
output = string.clone.extend self
output.wrapped_in = element
output.css = css
output
end
# Raises an exception if an object that doesn't respond to to_str is extended by Output,
# to prevent users from misuse. Use Module#remove_method to disable.
def extended o
warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str
end
def make_stylesheet css, in_tag = false
sheet = css.stylesheet
sheet = <<-CSS if in_tag
<style type="text/css">
#{sheet}
</style>
CSS
sheet
end
def page_template_for_css css
sheet = make_stylesheet css
PAGE.apply 'CSS', sheet
end
# Define a new wrapper. This is meta programming.
def wrapper *wrappers
wrappers.each do |wrapper|
define_method wrapper do |*args|
wrap wrapper, *args
end
define_method "#{wrapper}!".to_sym do |*args|
wrap! wrapper, *args
end
end
end
end
wrapper :div, :span, :page
def wrapped_in? element
wrapped_in == element
end
def wrapped_in
@wrapped_in ||= nil
end
attr_writer :wrapped_in
def wrap_in template
clone.wrap_in! template
end
def wrap_in! template
Template.wrap! self, template, 'CONTENT'
self
end
def apply_title! title
self.sub!(/(<title>)(<\/title>)/) { $1 + title + $2 }
self
end
def wrap! element, *args
return self if not element or element == wrapped_in
case element
when :div
raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
wrap_in! DIV
when :span
raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
wrap_in! SPAN
when :page
wrap! :div if wrapped_in? nil
raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div
wrap_in! Output.page_template_for_css(@css)
if args.first.is_a?(Hash) && title = args.first[:title]
apply_title! title
end
self
when nil
return self
else
raise "Unknown value %p for :wrap" % element
end
@wrapped_in = element
self
end
def wrap *args
clone.wrap!(*args)
end
def stylesheet in_tag = false
Output.make_stylesheet @css, in_tag
end
class Template < String
def self.wrap! str, template, target
target = Regexp.new(Regexp.escape("<%#{target}%>"))
if template =~ target
str[0,0] = $`
str << $'
else
raise "Template target <%%%p%%> not found" % target
end
end
def apply target, replacement
target = Regexp.new(Regexp.escape("<%#{target}%>"))
if self =~ target
Template.new($` + replacement + $')
else
raise "Template target <%%%p%%> not found" % target
end
end
module Simple
def ` str #` <-- for stupid editors
Template.new str
end
end
end
extend Template::Simple
#-- don't include the templates in docu
SPAN = `<span class="CodeRay"><%CONTENT%></span>`
DIV = <<-`DIV`
<div class="CodeRay">
<div class="code"><pre><%CONTENT%></pre></div>
</div>
DIV
TABLE = <<-`TABLE`
<table class="CodeRay"><tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><%LINE_NUMBERS%></pre></td>
<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><%CONTENT%></pre></td>
</tr></table>
TABLE
# title="double click to expand"
LIST = <<-`LIST`
<ol class="CodeRay">
<%CONTENT%>
</ol>
LIST
PAGE = <<-`PAGE`
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="de">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title></title>
<style type="text/css">
<%CSS%>
</style>
</head>
<body style="background-color: white;">
<%CONTENT%>
</body>
</html>
PAGE
end
end
end
end

View File

@ -1,69 +0,0 @@
($:.unshift '../..'; require 'coderay') unless defined? CodeRay
module CodeRay
module Encoders
# = JSON Encoder
class JSON < Encoder
register_for :json
FILE_EXTENSION = 'json'
protected
def setup options
begin
require 'json'
rescue LoadError
require 'rubygems'
require 'json'
end
@out = []
end
def text_token text, kind
{ :type => 'text', :text => text, :kind => kind }
end
def block_token action, kind
{ :type => 'block', :action => action, :kind => kind }
end
def finish options
@out.to_json
end
end
end
end
if $0 == __FILE__
$VERBOSE = true
$: << File.join(File.dirname(__FILE__), '..')
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
$:.delete '.'
require 'rubygems' if RUBY_VERSION < '1.9'
class JSONEncoderTest < Test::Unit::TestCase
def test_json_output
tokens = CodeRay.scan <<-RUBY, :ruby
puts "Hello world!"
RUBY
require 'json'
assert_equal [
{"type"=>"text", "text"=>"puts", "kind"=>"ident"},
{"type"=>"text", "text"=>" ", "kind"=>"space"},
{"type"=>"block", "action"=>"open", "kind"=>"string"},
{"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
{"type"=>"text", "text"=>"Hello world!", "kind"=>"content"},
{"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
{"type"=>"block", "action"=>"close", "kind"=>"string"},
{"type"=>"text", "text"=>"\n", "kind"=>"space"}
], JSON.load(tokens.json)
end
end

View File

@ -1,90 +0,0 @@
($:.unshift '../..'; require 'coderay') unless defined? CodeRay
module CodeRay
module Encoders
# Counts the LoC (Lines of Code). Returns an Integer >= 0.
#
# Alias: :loc
#
# Everything that is not comment, markup, doctype/shebang, or an empty line,
# is considered to be code.
#
# For example,
# * HTML files not containing JavaScript have 0 LoC
# * in a Java class without comments, LoC is the number of non-empty lines
#
# A Scanner class should define the token kinds that are not code in the
# KINDS_NOT_LOC constant, which defaults to [:comment, :doctype].
class LinesOfCode < Encoder
register_for :lines_of_code
NON_EMPTY_LINE = /^\s*\S.*$/
def compile tokens, options
if scanner = tokens.scanner
kinds_not_loc = scanner.class::KINDS_NOT_LOC
else
warn ArgumentError, 'Tokens have no scanner.' if $VERBOSE
kinds_not_loc = CodeRay::Scanners::Scanner::KINDS_NOT_LOC
end
code = tokens.token_class_filter :exclude => kinds_not_loc
@loc = code.text.scan(NON_EMPTY_LINE).size
end
def finish options
@loc
end
end
end
end
if $0 == __FILE__
$VERBOSE = true
$: << File.join(File.dirname(__FILE__), '..')
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
class LinesOfCodeTest < Test::Unit::TestCase
def test_creation
assert CodeRay::Encoders::LinesOfCode < CodeRay::Encoders::Encoder
filter = nil
assert_nothing_raised do
filter = CodeRay.encoder :loc
end
assert_kind_of CodeRay::Encoders::LinesOfCode, filter
assert_nothing_raised do
filter = CodeRay.encoder :lines_of_code
end
assert_kind_of CodeRay::Encoders::LinesOfCode, filter
end
def test_lines_of_code
tokens = CodeRay.scan <<-RUBY, :ruby
#!/usr/bin/env ruby
# a minimal Ruby program
puts "Hello world!"
RUBY
assert_equal 1, CodeRay::Encoders::LinesOfCode.new.encode_tokens(tokens)
assert_equal 1, tokens.lines_of_code
assert_equal 1, tokens.loc
end
def test_filtering_block_tokens
tokens = CodeRay::Tokens.new
tokens << ["Hello\n", :world]
tokens << ["Hello\n", :space]
tokens << ["Hello\n", :comment]
assert_equal 2, CodeRay::Encoders::LinesOfCode.new.encode_tokens(tokens)
assert_equal 2, tokens.lines_of_code
assert_equal 2, tokens.loc
end
end

View File

@ -1,20 +0,0 @@
module CodeRay
module Encoders
load :html
class Page < HTML
FILE_EXTENSION = 'html'
register_for :page
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \
:css => :class,
:wrap => :page,
:line_numbers => :table
end
end
end

View File

@ -1,19 +0,0 @@
module CodeRay
module Encoders
load :html
class Span < HTML
FILE_EXTENSION = 'span.html'
register_for :span
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \
:css => :style,
:wrap => :span
end
end
end

View File

@ -1,158 +0,0 @@
# encoders/term.rb
# By Rob Aldred (http://robaldred.co.uk)
# Based on idea by Nathan Weizenbaum (http://nex-3.com)
# MIT License (http://www.opensource.org/licenses/mit-license.php)
#
# A CodeRay encoder that outputs code highlighted for a color terminal.
# Check out http://robaldred.co.uk
module CodeRay
module Encoders
class Term < Encoder
register_for :term
TOKEN_COLORS = {
:annotation => '35',
:attribute_name => '33',
:attribute_name_fat => '33',
:attribute_value => '31',
:attribute_value_fat => '31',
:bin => '1;35',
:char => {:self => '36', :delimiter => '34'},
:class => '1;35',
:class_variable => '36',
:color => '32',
:comment => '37',
:complex => '34',
:constant => ['34', '4'],
:decoration => '35',
:definition => '1;32',
:directive => ['32', '4'],
:doc => '46',
:doctype => '1;30',
:doc_string => ['31', '4'],
:entity => '33',
:error => ['1;33', '41'],
:exception => '1;31',
:float => '1;35',
:function => '1;34',
:global_variable => '42',
:hex => '1;36',
:important => '1;31',
:include => '33',
:integer => '1;34',
:interpreted => '1;35',
:key => '35',
:label => '1;4',
:local_variable => '33',
:oct => '1;35',
:operator_name => '1;29',
:pre_constant => '1;36',
:pre_type => '1;30',
:predefined => ['4', '1;34'],
:preprocessor => '36',
:pseudo_class => '34',
:regexp => {
:content => '31',
:delimiter => '1;29',
:modifier => '35',
:function => '1;29'
},
:reserved => '1;31',
:shell => {
:self => '42',
:content => '1;29',
:delimiter => '37',
},
:string => {
:self => '32',
:modifier => '1;32',
:escape => '1;36',
:delimiter => '1;32',
},
:symbol => '1;32',
:tag => '34',
:tag_fat => '1;34',
:tag_special => ['34', '4'],
:type => '1;34',
:value => '36',
:variable => '34',
:insert => '42',
:delete => '41',
:change => '44',
:head => '45',
}
TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved]
TOKEN_COLORS[:method] = TOKEN_COLORS[:function]
TOKEN_COLORS[:imaginary] = TOKEN_COLORS[:complex]
TOKEN_COLORS[:open] = TOKEN_COLORS[:close] = TOKEN_COLORS[:nesting_delimiter] = TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter]
protected
def setup(options)
@out = ''
@opened = [nil]
@subcolors = nil
end
def finish(options)
super
end
def token text, type = :plain
case text
when nil
# raise 'Token with nil as text was given: %p' % [[text, type]]
when String
if color = (@subcolors || TOKEN_COLORS)[type]
color = color[:self] || return if Hash === color
@out << col(color) + text.gsub("\n", col(0) + "\n" + col(color)) + col(0)
@out << col(@subcolors[:self]) if @subcolors && @subcolors[:self]
else
@out << text
end
# token groups, eg. strings
when :open
@opened[0] = type
if color = TOKEN_COLORS[type]
if Hash === color
@subcolors = color
@out << col(color[:self]) if color[:self]
else
@subcolors = {}
@out << col(color)
end
end
@opened << type
when :close
if @opened.empty?
# nothing to close
else
@out << col(0) if (@subcolors || {})[:self]
@subcolors = nil
@opened.pop
end
# whole lines to be highlighted, eg. a added/modified/deleted lines in a diff
when :begin_line
when :end_line
else
raise 'unknown token kind: %p' % [text]
end
end
private
def col(color)
Array(color).map { |c| "\e[#{c}m" }.join
end
end
end
end

View File

@ -1,32 +0,0 @@
module CodeRay
module Encoders
class Text < Encoder
include Streamable
register_for :text
FILE_EXTENSION = 'txt'
DEFAULT_OPTIONS = {
:separator => ''
}
protected
def setup options
super
@sep = options[:separator]
end
def text_token text, kind
text + @sep
end
def finish options
super.chomp @sep
end
end
end
end

View File

@ -1,84 +0,0 @@
($:.unshift '../..'; require 'coderay') unless defined? CodeRay
module CodeRay
module Encoders
load :filter
class TokenClassFilter < Filter
include Streamable
register_for :token_class_filter
DEFAULT_OPTIONS = {
:exclude => [],
:include => :all
}
protected
def setup options
super
@exclude = options[:exclude]
@exclude = Array(@exclude) unless @exclude == :all
@include = options[:include]
@include = Array(@include) unless @include == :all
end
def include_text_token? text, kind
(@include == :all || @include.include?(kind)) &&
!(@exclude == :all || @exclude.include?(kind))
end
end
end
end
if $0 == __FILE__
$VERBOSE = true
$: << File.join(File.dirname(__FILE__), '..')
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
class TokenClassFilterTest < Test::Unit::TestCase
def test_creation
assert CodeRay::Encoders::TokenClassFilter < CodeRay::Encoders::Encoder
assert CodeRay::Encoders::TokenClassFilter < CodeRay::Encoders::Filter
filter = nil
assert_nothing_raised do
filter = CodeRay.encoder :token_class_filter
end
assert_instance_of CodeRay::Encoders::TokenClassFilter, filter
end
def test_filtering_text_tokens
tokens = CodeRay::Tokens.new
for i in 1..10
tokens << [i.to_s, :index]
tokens << [' ', :space] if i < 10
end
assert_equal 10, CodeRay::Encoders::TokenClassFilter.new.encode_tokens(tokens, :exclude => :space).size
assert_equal 10, tokens.token_class_filter(:exclude => :space).size
assert_equal 9, CodeRay::Encoders::TokenClassFilter.new.encode_tokens(tokens, :include => :space).size
assert_equal 9, tokens.token_class_filter(:include => :space).size
assert_equal 0, CodeRay::Encoders::TokenClassFilter.new.encode_tokens(tokens, :exclude => :all).size
assert_equal 0, tokens.token_class_filter(:exclude => :all).size
end
def test_filtering_block_tokens
tokens = CodeRay::Tokens.new
10.times do |i|
tokens << [:open, :index]
tokens << [i.to_s, :content]
tokens << [:close, :index]
end
assert_equal 20, CodeRay::Encoders::TokenClassFilter.new.encode_tokens(tokens, :include => :blubb).size
assert_equal 20, tokens.token_class_filter(:include => :blubb).size
assert_equal 30, CodeRay::Encoders::TokenClassFilter.new.encode_tokens(tokens, :exclude => :index).size
assert_equal 30, tokens.token_class_filter(:exclude => :index).size
end
end

View File

@ -1,22 +0,0 @@
module CodeRay
module Encoders
# = YAML Encoder
#
# Slow.
class YAML < Encoder
register_for :yaml
FILE_EXTENSION = 'yaml'
protected
def compile tokens, options
require 'yaml'
@out = tokens.to_a.to_yaml
end
end
end
end

View File

@ -1,255 +0,0 @@
#!/usr/bin/env ruby
module CodeRay
# = FileType
#
# A simple filetype recognizer.
#
# Copyright (c) 2006 by murphy (Kornelius Kalnbach) <murphy rubychan de>
#
# License:: LGPL / ask the author
# Version:: 0.1 (2005-09-01)
#
# == Documentation
#
# # determine the type of the given
# lang = FileType[ARGV.first]
#
# # return :plaintext if the file type is unknown
# lang = FileType.fetch ARGV.first, :plaintext
#
# # try the shebang line, too
# lang = FileType.fetch ARGV.first, :plaintext, true
module FileType
UnknownFileType = Class.new Exception
class << self
# Try to determine the file type of the file.
#
# +filename+ is a relative or absolute path to a file.
#
# The file itself is only accessed when +read_shebang+ is set to true.
# That means you can get filetypes from files that don't exist.
def [] filename, read_shebang = false
name = File.basename filename
ext = File.extname(name).sub(/^\./, '') # from last dot, delete the leading dot
ext2 = filename.to_s[/\.(.*)/, 1] # from first dot
type =
TypeFromExt[ext] ||
TypeFromExt[ext.downcase] ||
(TypeFromExt[ext2] if ext2) ||
(TypeFromExt[ext2.downcase] if ext2) ||
TypeFromName[name] ||
TypeFromName[name.downcase]
type ||= shebang(filename) if read_shebang
type
end
def shebang filename
begin
File.open filename, 'r' do |f|
if first_line = f.gets
if type = first_line[TypeFromShebang]
type.to_sym
end
end
end
rescue IOError
nil
end
end
# This works like Hash#fetch.
#
# If the filetype cannot be found, the +default+ value
# is returned.
def fetch filename, default = nil, read_shebang = false
if default and block_given?
warn 'block supersedes default value argument'
end
unless type = self[filename, read_shebang]
return yield if block_given?
return default if default
raise UnknownFileType, 'Could not determine type of %p.' % filename
end
type
end
end
TypeFromExt = {
'c' => :c,
'css' => :css,
'diff' => :diff,
'dpr' => :delphi,
'groovy' => :groovy,
'gvy' => :groovy,
'h' => :c,
'htm' => :html,
'html' => :html,
'html.erb' => :rhtml,
'java' => :java,
'js' => :java_script,
'json' => :json,
'mab' => :ruby,
'pas' => :delphi,
'patch' => :diff,
'php' => :php,
'php3' => :php,
'php4' => :php,
'php5' => :php,
'py' => :python,
'py3' => :python,
'pyw' => :python,
'rake' => :ruby,
'raydebug' => :debug,
'rb' => :ruby,
'rbw' => :ruby,
'rhtml' => :rhtml,
'rxml' => :ruby,
'sch' => :scheme,
'sql' => :sql,
'ss' => :scheme,
'xhtml' => :xhtml,
'xml' => :xml,
'yaml' => :yaml,
'yml' => :yaml,
}
for cpp_alias in %w[cc cpp cp cxx c++ C hh hpp h++ cu]
TypeFromExt[cpp_alias] = :cpp
end
TypeFromShebang = /\b(?:ruby|perl|python|sh)\b/
TypeFromName = {
'Rakefile' => :ruby,
'Rantfile' => :ruby,
}
end
end
if $0 == __FILE__
$VERBOSE = true
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
class FileTypeTests < Test::Unit::TestCase
include CodeRay
def test_fetch
assert_raise FileType::UnknownFileType do
FileType.fetch ''
end
assert_throws :not_found do
FileType.fetch '.' do
throw :not_found
end
end
assert_equal :default, FileType.fetch('c', :default)
stderr, fake_stderr = $stderr, Object.new
$err = ''
def fake_stderr.write x
$err << x
end
$stderr = fake_stderr
FileType.fetch('c', :default) { }
assert_equal "block supersedes default value argument\n", $err
$stderr = stderr
end
def test_ruby
assert_equal :ruby, FileType['test.rb']
assert_equal :ruby, FileType['test.java.rb']
assert_equal :java, FileType['test.rb.java']
assert_equal :ruby, FileType['C:\\Program Files\\x\\y\\c\\test.rbw']
assert_equal :ruby, FileType['/usr/bin/something/Rakefile']
assert_equal :ruby, FileType['~/myapp/gem/Rantfile']
assert_equal :ruby, FileType['./lib/tasks\repository.rake']
assert_not_equal :ruby, FileType['test_rb']
assert_not_equal :ruby, FileType['Makefile']
assert_not_equal :ruby, FileType['set.rb/set']
assert_not_equal :ruby, FileType['~/projects/blabla/rb']
end
def test_c
assert_equal :c, FileType['test.c']
assert_equal :c, FileType['C:\\Program Files\\x\\y\\c\\test.h']
assert_not_equal :c, FileType['test_c']
assert_not_equal :c, FileType['Makefile']
assert_not_equal :c, FileType['set.h/set']
assert_not_equal :c, FileType['~/projects/blabla/c']
end
def test_cpp
assert_equal :cpp, FileType['test.c++']
assert_equal :cpp, FileType['test.cxx']
assert_equal :cpp, FileType['test.hh']
assert_equal :cpp, FileType['test.hpp']
assert_equal :cpp, FileType['test.cu']
assert_equal :cpp, FileType['test.C']
assert_not_equal :cpp, FileType['test.c']
assert_not_equal :cpp, FileType['test.h']
end
def test_html
assert_equal :html, FileType['test.htm']
assert_equal :xhtml, FileType['test.xhtml']
assert_equal :xhtml, FileType['test.html.xhtml']
assert_equal :rhtml, FileType['_form.rhtml']
assert_equal :rhtml, FileType['_form.html.erb']
end
def test_yaml
assert_equal :yaml, FileType['test.yml']
assert_equal :yaml, FileType['test.yaml']
assert_equal :yaml, FileType['my.html.yaml']
assert_not_equal :yaml, FileType['YAML']
end
def test_pathname
require 'pathname'
pn = Pathname.new 'test.rb'
assert_equal :ruby, FileType[pn]
dir = Pathname.new '/etc/var/blubb'
assert_equal :ruby, FileType[dir + pn]
assert_equal :cpp, FileType[dir + 'test.cpp']
end
def test_no_shebang
dir = './test'
if File.directory? dir
Dir.chdir dir do
assert_equal :c, FileType['test.c']
end
end
end
def test_shebang_empty_file
require 'tmpdir'
tmpfile = File.join(Dir.tmpdir, 'bla')
File.open(tmpfile, 'w') { } # touch
assert_equal nil, FileType[tmpfile]
end
def test_shebang
require 'tmpdir'
tmpfile = File.join(Dir.tmpdir, 'bla')
File.open(tmpfile, 'w') { |f| f.puts '#!/usr/bin/env ruby' }
assert_equal :ruby, FileType[tmpfile, true]
end
end

View File

@ -1,123 +0,0 @@
# =GZip Simple
#
# A simplified interface to the gzip library +zlib+ (from the Ruby Standard Library.)
#
# Author: murphy (mail to murphy rubychan de)
#
# Version: 0.2 (2005.may.28)
#
# ==Documentation
#
# See +GZip+ module and the +String+ extensions.
#
module GZip
require 'zlib'
# The default zipping level. 7 zips good and fast.
DEFAULT_GZIP_LEVEL = 7
# Unzips the given string +s+.
#
# Example:
# require 'gzip_simple'
# print GZip.gunzip(File.read('adresses.gz'))
def GZip.gunzip s
Zlib::Inflate.inflate s
end
# Zips the given string +s+.
#
# Example:
# require 'gzip_simple'
# File.open('adresses.gz', 'w') do |file
# file.write GZip.gzip('Mum: 0123 456 789', 9)
# end
#
# If you provide a +level+, you can control how strong
# the string is compressed:
# - 0: no compression, only convert to gzip format
# - 1: compress fast
# - 7: compress more, but still fast (default)
# - 8: compress more, slower
# - 9: compress best, very slow
def GZip.gzip s, level = DEFAULT_GZIP_LEVEL
Zlib::Deflate.new(level).deflate s, Zlib::FINISH
end
end
# String extensions to use the GZip module.
#
# The methods gzip and gunzip provide an even more simple
# interface to the ZLib:
#
# # create a big string
# x = 'a' * 1000
#
# # zip it
# x_gz = x.gzip
#
# # test the result
# puts 'Zipped %d bytes to %d bytes.' % [x.size, x_gz.size]
# #-> Zipped 1000 bytes to 19 bytes.
#
# # unzipping works
# p x_gz.gunzip == x #-> true
class String
# Returns the string, unzipped.
# See GZip.gunzip
def gunzip
GZip.gunzip self
end
# Replaces the string with its unzipped value.
# See GZip.gunzip
def gunzip!
replace gunzip
end
# Returns the string, zipped.
# +level+ is the gzip compression level, see GZip.gzip.
def gzip level = GZip::DEFAULT_GZIP_LEVEL
GZip.gzip self, level
end
# Replaces the string with its zipped value.
# See GZip.gzip.
def gzip!(*args)
replace gzip(*args)
end
end
if $0 == __FILE__
eval DATA.read, nil, $0, __LINE__+4
end
__END__
#CODE
# Testing / Benchmark
x = 'a' * 1000
x_gz = x.gzip
puts 'Zipped %d bytes to %d bytes.' % [x.size, x_gz.size] #-> Zipped 1000 bytes to 19 bytes.
p x_gz.gunzip == x #-> true
require 'benchmark'
INFO = 'packed to %0.3f%%' # :nodoc:
x = Array.new(100000) { rand(255).chr + 'aaaaaaaaa' + rand(255).chr }.join
Benchmark.bm(10) do |bm|
for level in 0..9
bm.report "zip #{level}" do
$x = x.gzip level
end
puts INFO % [100.0 * $x.size / x.size]
end
bm.report 'zip' do
$x = x.gzip
end
puts INFO % [100.0 * $x.size / x.size]
bm.report 'unzip' do
$x.gunzip
end
end

View File

@ -1,349 +0,0 @@
module CodeRay
# = PluginHost
#
# A simple subclass plugin system.
#
# Example:
# class Generators < PluginHost
# plugin_path 'app/generators'
# end
#
# class Generator
# extend Plugin
# PLUGIN_HOST = Generators
# end
#
# class FancyGenerator < Generator
# register_for :fancy
# end
#
# Generators[:fancy] #-> FancyGenerator
# # or
# CodeRay.require_plugin 'Generators/fancy'
module PluginHost
# Raised if Encoders::[] fails because:
# * a file could not be found
# * the requested Encoder is not registered
PluginNotFound = Class.new Exception
HostNotFound = Class.new Exception
PLUGIN_HOSTS = []
PLUGIN_HOSTS_BY_ID = {} # dummy hash
# Loads all plugins using list and load.
def load_all
for plugin in list
load plugin
end
end
# Returns the Plugin for +id+.
#
# Example:
# yaml_plugin = MyPluginHost[:yaml]
def [] id, *args, &blk
plugin = validate_id(id)
begin
plugin = plugin_hash.[] plugin, *args, &blk
end while plugin.is_a? Symbol
plugin
end
# Alias for +[]+.
alias load []
def require_helper plugin_id, helper_name
path = path_to File.join(plugin_id, helper_name)
require path
end
class << self
# Adds the module/class to the PLUGIN_HOSTS list.
def extended mod
PLUGIN_HOSTS << mod
end
# Warns you that you should not #include this module.
def included mod
warn "#{name} should not be included. Use extend."
end
# Find the PluginHost for host_id.
def host_by_id host_id
unless PLUGIN_HOSTS_BY_ID.default_proc
ph = Hash.new do |h, a_host_id|
for host in PLUGIN_HOSTS
h[host.host_id] = host
end
h.fetch a_host_id, nil
end
PLUGIN_HOSTS_BY_ID.replace ph
end
PLUGIN_HOSTS_BY_ID[host_id]
end
end
# The path where the plugins can be found.
def plugin_path *args
unless args.empty?
@plugin_path = File.expand_path File.join(*args)
load_map
end
@plugin_path
end
# The host's ID.
#
# If PLUGIN_HOST_ID is not set, it is simply the class name.
def host_id
if self.const_defined? :PLUGIN_HOST_ID
self::PLUGIN_HOST_ID
else
name
end
end
# Map a plugin_id to another.
#
# Usage: Put this in a file plugin_path/_map.rb.
#
# class MyColorHost < PluginHost
# map :navy => :dark_blue,
# :maroon => :brown,
# :luna => :moon
# end
def map hash
for from, to in hash
from = validate_id from
to = validate_id to
plugin_hash[from] = to unless plugin_hash.has_key? from
end
end
# Define the default plugin to use when no plugin is found
# for a given id.
#
# See also map.
#
# class MyColorHost < PluginHost
# map :navy => :dark_blue
# default :gray
# end
def default id = nil
if id
id = validate_id id
plugin_hash[nil] = id
else
plugin_hash[nil]
end
end
# Every plugin must register itself for one or more
# +ids+ by calling register_for, which calls this method.
#
# See Plugin#register_for.
def register plugin, *ids
for id in ids
unless id.is_a? Symbol
raise ArgumentError,
"id must be a Symbol, but it was a #{id.class}"
end
plugin_hash[validate_id(id)] = plugin
end
end
# A Hash of plugion_id => Plugin pairs.
def plugin_hash
@plugin_hash ||= create_plugin_hash
end
# Returns an array of all .rb files in the plugin path.
#
# The extension .rb is not included.
def list
Dir[path_to('*')].select do |file|
File.basename(file)[/^(?!_)\w+\.rb$/]
end.map do |file|
File.basename file, '.rb'
end
end
# Makes a map of all loaded plugins.
def inspect
map = plugin_hash.dup
map.each do |id, plugin|
map[id] = plugin.to_s[/(?>\w+)$/]
end
"#{name}[#{host_id}]#{map.inspect}"
end
protected
# Created a new plugin list and stores it to @plugin_hash.
def create_plugin_hash
@plugin_hash =
Hash.new do |h, plugin_id|
id = validate_id(plugin_id)
path = path_to id
begin
require path
rescue LoadError => boom
if h.has_key? nil # default plugin
h[id] = h[nil]
else
raise PluginNotFound, 'Could not load plugin %p: %s' % [id, boom]
end
else
# Plugin should have registered by now
unless h.has_key? id
raise PluginNotFound,
"No #{self.name} plugin for #{id.inspect} found in #{path}."
end
end
h[id]
end
end
# Loads the map file (see map).
#
# This is done automatically when plugin_path is called.
def load_map
mapfile = path_to '_map'
if File.exist? mapfile
require mapfile
elsif $VERBOSE
warn 'no _map.rb found for %s' % name
end
end
# Returns the Plugin for +id+.
# Use it like Hash#fetch.
#
# Example:
# yaml_plugin = MyPluginHost[:yaml, :default]
def fetch id, *args, &blk
plugin_hash.fetch validate_id(id), *args, &blk
end
# Returns the expected path to the plugin file for the given id.
def path_to plugin_id
File.join plugin_path, "#{plugin_id}.rb"
end
# Converts +id+ to a Symbol if it is a String,
# or returns +id+ if it already is a Symbol.
#
# Raises +ArgumentError+ for all other objects, or if the
# given String includes non-alphanumeric characters (\W).
def validate_id id
if id.is_a? Symbol or id.nil?
id
elsif id.is_a? String
if id[/\w+/] == id
id.downcase.to_sym
else
raise ArgumentError, "Invalid id: '#{id}' given."
end
else
raise ArgumentError,
"String or Symbol expected, but #{id.class} given."
end
end
end
# = Plugin
#
# Plugins have to include this module.
#
# IMPORTANT: use extend for this module.
#
# Example: see PluginHost.
module Plugin
def included mod
warn "#{name} should not be included. Use extend."
end
# Register this class for the given langs.
# Example:
# class MyPlugin < PluginHost::BaseClass
# register_for :my_id
# ...
# end
#
# See PluginHost.register.
def register_for *ids
plugin_host.register self, *ids
end
# Returns the title of the plugin, or sets it to the
# optional argument +title+.
def title title = nil
if title
@title = title.to_s
else
@title ||= name[/([^:]+)$/, 1]
end
end
# The host for this Plugin class.
def plugin_host host = nil
if host and not host.is_a? PluginHost
raise ArgumentError,
"PluginHost expected, but #{host.class} given."
end
self.const_set :PLUGIN_HOST, host if host
self::PLUGIN_HOST
end
# Require some helper files.
#
# Example:
#
# class MyPlugin < PluginHost::BaseClass
# register_for :my_id
# helper :my_helper
#
# The above example loads the file myplugin/my_helper.rb relative to the
# file in which MyPlugin was defined.
#
# You can also load a helper from a different plugin:
#
# helper 'other_plugin/helper_name'
def helper *helpers
for helper in helpers
if helper.is_a?(String) && helper[/\//]
self::PLUGIN_HOST.require_helper $`, $'
else
self::PLUGIN_HOST.require_helper plugin_id, helper.to_s
end
end
end
# Returns the pulgin id used by the engine.
def plugin_id
name[/\w+$/].downcase
end
end
# Convenience method for plugin loading.
# The syntax used is:
#
# CodeRay.require_plugin '<Host ID>/<Plugin ID>'
#
# Returns the loaded plugin.
def self.require_plugin path
host_id, plugin_id = path.split '/', 2
host = PluginHost.host_by_id(host_id)
raise PluginHost::HostNotFound,
"No host for #{host_id.inspect} found." unless host
host.load plugin_id
end
end

View File

@ -1,138 +0,0 @@
module CodeRay
# = WordList
#
# <b>A Hash subclass designed for mapping word lists to token types.</b>
#
# Copyright (c) 2006 by murphy (Kornelius Kalnbach) <murphy rubychan de>
#
# License:: LGPL / ask the author
# Version:: 1.1 (2006-Oct-19)
#
# A WordList is a Hash with some additional features.
# It is intended to be used for keyword recognition.
#
# WordList is highly optimized to be used in Scanners,
# typically to decide whether a given ident is a special token.
#
# For case insensitive words use CaseIgnoringWordList.
#
# Example:
#
# # define word arrays
# RESERVED_WORDS = %w[
# asm break case continue default do else
# ...
# ]
#
# PREDEFINED_TYPES = %w[
# int long short char void
# ...
# ]
#
# PREDEFINED_CONSTANTS = %w[
# EOF NULL ...
# ]
#
# # make a WordList
# IDENT_KIND = WordList.new(:ident).
# add(RESERVED_WORDS, :reserved).
# add(PREDEFINED_TYPES, :pre_type).
# add(PREDEFINED_CONSTANTS, :pre_constant)
#
# ...
#
# def scan_tokens tokens, options
# ...
#
# elsif scan(/[A-Za-z_][A-Za-z_0-9]*/)
# # use it
# kind = IDENT_KIND[match]
# ...
class WordList < Hash
# Creates a new WordList with +default+ as default value.
#
# You can activate +caching+ to store the results for every [] request.
#
# With caching, methods like +include?+ or +delete+ may no longer behave
# as you expect. Therefore, it is recommended to use the [] method only.
def initialize default = false, caching = false, &block
if block
raise ArgumentError, 'Can\'t combine block with caching.' if caching
super(&block)
else
if caching
super() do |h, k|
h[k] = h.fetch k, default
end
else
super default
end
end
end
# Add words to the list and associate them with +kind+.
#
# Returns +self+, so you can concat add calls.
def add words, kind = true
words.each do |word|
self[word] = kind
end
self
end
end
# A CaseIgnoringWordList is like a WordList, only that
# keys are compared case-insensitively.
#
# Ignoring the text case is realized by sending the +downcase+ message to
# all keys.
#
# Caching usually makes a CaseIgnoringWordList faster, but it has to be
# activated explicitely.
class CaseIgnoringWordList < WordList
# Creates a new case-insensitive WordList with +default+ as default value.
#
# You can activate caching to store the results for every [] request.
# This speeds up subsequent lookups for the same word, but also
# uses memory.
def initialize default = false, caching = false
if caching
super(default, false) do |h, k|
h[k] = h.fetch k.downcase, default
end
else
super(default, false)
extend Uncached
end
end
module Uncached # :nodoc:
def [] key
super(key.downcase)
end
end
# Add +words+ to the list and associate them with +kind+.
def add words, kind = true
words.each do |word|
self[word.downcase] = kind
end
self
end
end
end
__END__
# check memory consumption
END {
ObjectSpace.each_object(CodeRay::CaseIgnoringWordList) do |wl|
p wl.inject(0) { |memo, key, value| memo + key.size + 24 }
end
}

View File

@ -1,298 +0,0 @@
module CodeRay
require 'coderay/helpers/plugin'
# = Scanners
#
# This module holds the Scanner class and its subclasses.
# For example, the Ruby scanner is named CodeRay::Scanners::Ruby
# can be found in coderay/scanners/ruby.
#
# Scanner also provides methods and constants for the register
# mechanism and the [] method that returns the Scanner class
# belonging to the given lang.
#
# See PluginHost.
module Scanners
extend PluginHost
plugin_path File.dirname(__FILE__), 'scanners'
require 'strscan'
# = Scanner
#
# The base class for all Scanners.
#
# It is a subclass of Ruby's great +StringScanner+, which
# makes it easy to access the scanning methods inside.
#
# It is also +Enumerable+, so you can use it like an Array of
# Tokens:
#
# require 'coderay'
#
# c_scanner = CodeRay::Scanners[:c].new "if (*p == '{') nest++;"
#
# for text, kind in c_scanner
# puts text if kind == :operator
# end
#
# # prints: (*==)++;
#
# OK, this is a very simple example :)
# You can also use +map+, +any?+, +find+ and even +sort_by+,
# if you want.
class Scanner < StringScanner
extend Plugin
plugin_host Scanners
# Raised if a Scanner fails while scanning
ScanError = Class.new(Exception)
require 'coderay/helpers/word_list'
# The default options for all scanner classes.
#
# Define @default_options for subclasses.
DEFAULT_OPTIONS = { :stream => false }
KINDS_NOT_LOC = [:comment, :doctype]
class << self
# Returns if the Scanner can be used in streaming mode.
def streamable?
is_a? Streamable
end
def normify code
code = code.to_s
if code.respond_to?(:encoding) && (code.encoding.name != 'UTF-8' || !code.valid_encoding?)
code = code.dup
original_encoding = code.encoding
code.force_encoding 'Windows-1252'
unless code.valid_encoding?
code.force_encoding original_encoding
if code.encoding.name == 'UTF-8'
code.encode! 'UTF-16BE', :invalid => :replace, :undef => :replace, :replace => '?'
end
code.encode! 'UTF-8', :invalid => :replace, :undef => :replace, :replace => '?'
end
end
code.to_unix
end
def file_extension extension = nil
if extension
@file_extension = extension.to_s
else
@file_extension ||= plugin_id.to_s
end
end
end
=begin
## Excluded for speed reasons; protected seems to make methods slow.
# Save the StringScanner methods from being called.
# This would not be useful for highlighting.
strscan_public_methods =
StringScanner.instance_methods -
StringScanner.ancestors[1].instance_methods
protected(*strscan_public_methods)
=end
# Create a new Scanner.
#
# * +code+ is the input String and is handled by the superclass
# StringScanner.
# * +options+ is a Hash with Symbols as keys.
# It is merged with the default options of the class (you can
# overwrite default options here.)
# * +block+ is the callback for streamed highlighting.
#
# If you set :stream to +true+ in the options, the Scanner uses a
# TokenStream with the +block+ as callback to handle the tokens.
#
# Else, a Tokens object is used.
def initialize code='', options = {}, &block
raise "I am only the basic Scanner class. I can't scan "\
"anything. :( Use my subclasses." if self.class == Scanner
@options = self.class::DEFAULT_OPTIONS.merge options
super Scanner.normify(code)
@tokens = options[:tokens]
if @options[:stream]
warn "warning in CodeRay::Scanner.new: :stream is set, "\
"but no block was given" unless block_given?
raise NotStreamableError, self unless kind_of? Streamable
@tokens ||= TokenStream.new(&block)
else
warn "warning in CodeRay::Scanner.new: Block given, "\
"but :stream is #{@options[:stream]}" if block_given?
@tokens ||= Tokens.new
end
@tokens.scanner = self
setup
end
def reset
super
reset_instance
end
def string= code
code = Scanner.normify(code)
if defined?(RUBY_DESCRIPTION) && RUBY_DESCRIPTION['rubinius 1.0.1']
reset_state
@string = code
else
super code
end
reset_instance
end
# More mnemonic accessor name for the input string.
alias code string
alias code= string=
# Returns the Plugin ID for this scanner.
def lang
self.class.plugin_id
end
# Scans the code and returns all tokens in a Tokens object.
def tokenize new_string=nil, options = {}
options = @options.merge(options)
self.string = new_string if new_string
@cached_tokens =
if @options[:stream] # :stream must have been set already
reset unless new_string
scan_tokens @tokens, options
@tokens
else
scan_tokens @tokens, options
end
end
def tokens
@cached_tokens ||= tokenize
end
# Whether the scanner is in streaming mode.
def streaming?
!!@options[:stream]
end
# Traverses the tokens.
def each &block
raise ArgumentError,
'Cannot traverse TokenStream.' if @options[:stream]
tokens.each(&block)
end
include Enumerable
# The current line position of the scanner.
#
# Beware, this is implemented inefficiently. It should be used
# for debugging only.
def line
string[0..pos].count("\n") + 1
end
def column pos = self.pos
return 0 if pos <= 0
string = string()
if string.respond_to?(:bytesize) && (defined?(@bin_string) || string.bytesize != string.size)
@bin_string ||= string.dup.force_encoding('binary')
string = @bin_string
end
pos - (string.rindex(?\n, pos) || 0)
end
def marshal_dump
@options
end
def marshal_load options
@options = options
end
protected
# Can be implemented by subclasses to do some initialization
# that has to be done once per instance.
#
# Use reset for initialization that has to be done once per
# scan.
def setup
end
# This is the central method, and commonly the only one a
# subclass implements.
#
# Subclasses must implement this method; it must return +tokens+
# and must only use Tokens#<< for storing scanned tokens!
def scan_tokens tokens, options
raise NotImplementedError,
"#{self.class}#scan_tokens not implemented."
end
def reset_instance
@tokens.clear unless @options[:keep_tokens]
@cached_tokens = nil
@bin_string = nil if defined? @bin_string
end
# Scanner error with additional status information
def raise_inspect msg, tokens, state = 'No state given!', ambit = 30
raise ScanError, <<-EOE % [
***ERROR in %s: %s (after %d tokens)
tokens:
%s
current line: %d column: %d pos: %d
matched: %p state: %p
bol? = %p, eos? = %p
surrounding code:
%p ~~ %p
***ERROR***
EOE
File.basename(caller[0]),
msg,
tokens.size,
tokens.last(10).map { |t| t.inspect }.join("\n"),
line, column, pos,
matched, state, bol?, eos?,
string[pos - ambit, ambit],
string[pos, ambit],
]
end
end
end
end
class String
# I love this hack. It seems to silence all dos/unix/mac newline problems.
def to_unix
if index ?\r
gsub(/\r\n?/, "\n")
else
self
end
end
end

View File

@ -1,23 +0,0 @@
module CodeRay
module Scanners
map \
:h => :c,
:cplusplus => :cpp,
:'c++' => :cpp,
:ecma => :java_script,
:ecmascript => :java_script,
:ecma_script => :java_script,
:irb => :ruby,
:javascript => :java_script,
:js => :java_script,
:nitro => :nitro_xhtml,
:pascal => :delphi,
:plain => :plaintext,
:xhtml => :html,
:yml => :yaml
default :plain
end
end

View File

@ -1,203 +0,0 @@
module CodeRay
module Scanners
class C < Scanner
include Streamable
register_for :c
file_extension 'c'
RESERVED_WORDS = [
'asm', 'break', 'case', 'continue', 'default', 'do',
'else', 'enum', 'for', 'goto', 'if', 'return',
'sizeof', 'struct', 'switch', 'typedef', 'union', 'while',
'restrict', # added in C99
]
PREDEFINED_TYPES = [
'int', 'long', 'short', 'char',
'signed', 'unsigned', 'float', 'double',
'bool', 'complex', # added in C99
]
PREDEFINED_CONSTANTS = [
'EOF', 'NULL',
'true', 'false', # added in C99
]
DIRECTIVES = [
'auto', 'extern', 'register', 'static', 'void',
'const', 'volatile', # added in C89
'inline', # added in C99
]
IDENT_KIND = WordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_TYPES, :pre_type).
add(DIRECTIVES, :directive).
add(PREDEFINED_CONSTANTS, :pre_constant)
ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
def scan_tokens tokens, options
state = :initial
label_expected = true
case_expected = false
label_expected_before_preproc_line = nil
in_preproc_line = false
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
if in_preproc_line && match != "\\\n" && match.index(?\n)
in_preproc_line = false
label_expected = label_expected_before_preproc_line
end
tokens << [match, :space]
next
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
kind = :comment
elsif match = scan(/ \# \s* if \s* 0 /x)
match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
kind = :comment
elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x)
label_expected = match =~ /[;\{\}]/
if case_expected
label_expected = true if match == ':'
case_expected = false
end
kind = :operator
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
kind = IDENT_KIND[match]
if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/)
kind = :label
match << matched
else
label_expected = false
if kind == :reserved
case match
when 'case', 'default'
case_expected = true
end
end
end
elsif scan(/\$/)
kind = :ident
elsif match = scan(/L?"/)
tokens << [:open, :string]
if match[0] == ?L
tokens << ['L', :modifier]
match = '"'
end
state = :string
kind = :delimiter
elsif scan(/#[ \t]*(\w*)/)
kind = :preprocessor
in_preproc_line = true
label_expected_before_preproc_line = label_expected
state = :include_expected if self[1] == 'include'
elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
label_expected = false
kind = :char
elsif scan(/0[xX][0-9A-Fa-f]+/)
label_expected = false
kind = :hex
elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
label_expected = false
kind = :oct
elsif scan(/(?:\d+)(?![.eEfF])L?L?/)
label_expected = false
kind = :integer
elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
label_expected = false
kind = :float
else
getch
kind = :error
end
when :string
if scan(/[^\\\n"]+/)
kind = :content
elsif scan(/"/)
tokens << ['"', :delimiter]
tokens << [:close, :string]
state = :initial
label_expected = false
next
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif scan(/ \\ | $ /x)
tokens << [:close, :string]
kind = :error
state = :initial
label_expected = false
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
when :include_expected
if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
kind = :include
state = :initial
elsif match = scan(/\s+/)
kind = :space
state = :initial if match.index ?\n
else
state = :initial
next
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if state == :string
tokens << [:close, :string]
end
tokens
end
end
end
end

View File

@ -1,228 +0,0 @@
module CodeRay
module Scanners
class CPlusPlus < Scanner
include Streamable
register_for :cpp
file_extension 'cpp'
title 'C++'
# http://www.cppreference.com/wiki/keywords/start
RESERVED_WORDS = [
'and', 'and_eq', 'asm', 'bitand', 'bitor', 'break',
'case', 'catch', 'class', 'compl', 'const_cast',
'continue', 'default', 'delete', 'do', 'dynamic_cast', 'else',
'enum', 'export', 'for', 'goto', 'if', 'namespace', 'new',
'not', 'not_eq', 'or', 'or_eq', 'reinterpret_cast', 'return',
'sizeof', 'static_cast', 'struct', 'switch', 'template',
'throw', 'try', 'typedef', 'typeid', 'typename', 'union',
'while', 'xor', 'xor_eq'
]
PREDEFINED_TYPES = [
'bool', 'char', 'double', 'float', 'int', 'long',
'short', 'signed', 'unsigned', 'wchar_t', 'string'
]
PREDEFINED_CONSTANTS = [
'false', 'true',
'EOF', 'NULL',
]
PREDEFINED_VARIABLES = [
'this'
]
DIRECTIVES = [
'auto', 'const', 'explicit', 'extern', 'friend', 'inline', 'mutable', 'operator',
'private', 'protected', 'public', 'register', 'static', 'using', 'virtual', 'void',
'volatile'
]
IDENT_KIND = WordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_TYPES, :pre_type).
add(PREDEFINED_VARIABLES, :local_variable).
add(DIRECTIVES, :directive).
add(PREDEFINED_CONSTANTS, :pre_constant)
ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
def scan_tokens tokens, options
state = :initial
label_expected = true
case_expected = false
label_expected_before_preproc_line = nil
in_preproc_line = false
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
if in_preproc_line && match != "\\\n" && match.index(?\n)
in_preproc_line = false
label_expected = label_expected_before_preproc_line
end
tokens << [match, :space]
next
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
kind = :comment
elsif match = scan(/ \# \s* if \s* 0 /x)
match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
kind = :comment
elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x)
label_expected = match =~ /[;\{\}]/
if case_expected
label_expected = true if match == ':'
case_expected = false
end
kind = :operator
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
kind = IDENT_KIND[match]
if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/)
kind = :label
match << matched
else
label_expected = false
if kind == :reserved
case match
when 'class'
state = :class_name_expected
when 'case', 'default'
case_expected = true
end
end
end
elsif scan(/\$/)
kind = :ident
elsif match = scan(/L?"/)
tokens << [:open, :string]
if match[0] == ?L
tokens << ['L', :modifier]
match = '"'
end
state = :string
kind = :delimiter
elsif scan(/#[ \t]*(\w*)/)
kind = :preprocessor
in_preproc_line = true
label_expected_before_preproc_line = label_expected
state = :include_expected if self[1] == 'include'
elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
label_expected = false
kind = :char
elsif scan(/0[xX][0-9A-Fa-f]+/)
label_expected = false
kind = :hex
elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
label_expected = false
kind = :oct
elsif scan(/(?:\d+)(?![.eEfF])L?L?/)
label_expected = false
kind = :integer
elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
label_expected = false
kind = :float
else
getch
kind = :error
end
when :string
if scan(/[^\\"]+/)
kind = :content
elsif scan(/"/)
tokens << ['"', :delimiter]
tokens << [:close, :string]
state = :initial
label_expected = false
next
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif scan(/ \\ | $ /x)
tokens << [:close, :string]
kind = :error
state = :initial
label_expected = false
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
when :include_expected
if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
kind = :include
state = :initial
elsif match = scan(/\s+/)
kind = :space
state = :initial if match.index ?\n
else
state = :initial
next
end
when :class_name_expected
if scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
kind = :class
state = :initial
elsif match = scan(/\s+/)
kind = :space
else
getch
kind = :error
state = :initial
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if state == :string
tokens << [:close, :string]
end
tokens
end
end
end
end

View File

@ -1,209 +0,0 @@
module CodeRay
module Scanners
class CSS < Scanner
register_for :css
KINDS_NOT_LOC = [
:comment,
:class, :pseudo_class, :type,
:constant, :directive,
:key, :value, :operator, :color, :float,
:error, :important,
]
module RE
Hex = /[0-9a-fA-F]/
Unicode = /\\#{Hex}{1,6}(?:\r\n|\s)?/ # differs from standard because it allows uppercase hex too
Escape = /#{Unicode}|\\[^\r\n\f0-9a-fA-F]/
NMChar = /[-_a-zA-Z0-9]|#{Escape}/
NMStart = /[_a-zA-Z]|#{Escape}/
NL = /\r\n|\r|\n|\f/
String1 = /"(?:[^\n\r\f\\"]|\\#{NL}|#{Escape})*"?/ # FIXME: buggy regexp
String2 = /'(?:[^\n\r\f\\']|\\#{NL}|#{Escape})*'?/ # FIXME: buggy regexp
String = /#{String1}|#{String2}/
HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/
Color = /#{HexColor}/
Num = /-?(?:[0-9]+|[0-9]*\.[0-9]+)/
Name = /#{NMChar}+/
Ident = /-?#{NMStart}#{NMChar}*/
AtKeyword = /@#{Ident}/
Percentage = /#{Num}%/
reldimensions = %w[em ex px]
absdimensions = %w[in cm mm pt pc]
Unit = Regexp.union(*(reldimensions + absdimensions))
Dimension = /#{Num}#{Unit}/
Comment = %r! /\* (?: .*? \*/ | .* ) !mx
Function = /(?:url|alpha)\((?:[^)\n\r\f]|\\\))*\)?/
Id = /##{Name}/
Class = /\.#{Name}/
PseudoClass = /:#{Name}/
AttributeSelector = /\[[^\]]*\]?/
end
def scan_tokens tokens, options
value_expected = nil
states = [:initial]
until eos?
kind = nil
match = nil
if scan(/\s+/)
kind = :space
elsif case states.last
when :initial, :media
if scan(/(?>#{RE::Ident})(?!\()|\*/ox)
kind = :type
elsif scan RE::Class
kind = :class
elsif scan RE::Id
kind = :constant
elsif scan RE::PseudoClass
kind = :pseudo_class
elsif match = scan(RE::AttributeSelector)
# TODO: Improve highlighting inside of attribute selectors.
tokens << [:open, :string]
tokens << [match[0,1], :delimiter]
tokens << [match[1..-2], :content] if match.size > 2
tokens << [match[-1,1], :delimiter] if match[-1] == ?]
tokens << [:close, :string]
next
elsif match = scan(/@media/)
kind = :directive
states.push :media_before_name
end
when :block
if scan(/(?>#{RE::Ident})(?!\()/ox)
if value_expected
kind = :value
else
kind = :key
end
end
when :media_before_name
if scan RE::Ident
kind = :type
states[-1] = :media_after_name
end
when :media_after_name
if scan(/\{/)
kind = :operator
states[-1] = :media
end
when :comment
if scan(/(?:[^*\s]|\*(?!\/))+/)
kind = :comment
elsif scan(/\*\//)
kind = :comment
states.pop
elsif scan(/\s+/)
kind = :space
end
else
raise_inspect 'Unknown state', tokens
end
elsif scan(/\/\*/)
kind = :comment
states.push :comment
elsif scan(/\{/)
value_expected = false
kind = :operator
states.push :block
elsif scan(/\}/)
value_expected = false
if states.last == :block || states.last == :media
kind = :operator
states.pop
else
kind = :error
end
elsif match = scan(/#{RE::String}/o)
tokens << [:open, :string]
tokens << [match[0, 1], :delimiter]
tokens << [match[1..-2], :content] if match.size > 2
tokens << [match[-1, 1], :delimiter] if match.size >= 2
tokens << [:close, :string]
next
elsif match = scan(/#{RE::Function}/o)
tokens << [:open, :string]
start = match[/^\w+\(/]
tokens << [start, :delimiter]
if match[-1] == ?)
tokens << [match[start.size..-2], :content]
tokens << [')', :delimiter]
else
tokens << [match[start.size..-1], :content]
end
tokens << [:close, :string]
next
elsif scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox)
kind = :float
elsif scan(/#{RE::Color}/o)
kind = :color
elsif scan(/! *important/)
kind = :important
elsif scan(/rgb\([^()\n]*\)?/)
kind = :color
elsif scan(/#{RE::AtKeyword}/o)
kind = :directive
elsif match = scan(/ [+>:;,.=()\/] /x)
if match == ':'
value_expected = true
elsif match == ';'
value_expected = false
end
kind = :operator
else
getch
kind = :error
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
tokens
end
end
end
end

View File

@ -1,62 +0,0 @@
module CodeRay
module Scanners
# = Debug Scanner
class Debug < Scanner
include Streamable
register_for :debug
file_extension 'raydebug'
title 'CodeRay Token Dump'
protected
def scan_tokens tokens, options
opened_tokens = []
until eos?
kind = nil
match = nil
if scan(/\s+/)
tokens << [matched, :space]
next
elsif scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \) /x)
kind = self[1].to_sym
match = self[2].gsub(/\\(.)/, '\1')
elsif scan(/ (\w+) < /x)
kind = self[1].to_sym
opened_tokens << kind
match = :open
elsif !opened_tokens.empty? && scan(/ > /x)
kind = opened_tokens.pop || :error
match = :close
else
kind = :error
getch
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
tokens
end
end
end
end

View File

@ -1,150 +0,0 @@
module CodeRay
module Scanners
class Delphi < Scanner
register_for :delphi
file_extension 'pas'
RESERVED_WORDS = [
'and', 'array', 'as', 'at', 'asm', 'at', 'begin', 'case', 'class',
'const', 'constructor', 'destructor', 'dispinterface', 'div', 'do',
'downto', 'else', 'end', 'except', 'exports', 'file', 'finalization',
'finally', 'for', 'function', 'goto', 'if', 'implementation', 'in',
'inherited', 'initialization', 'inline', 'interface', 'is', 'label',
'library', 'mod', 'nil', 'not', 'object', 'of', 'or', 'out', 'packed',
'procedure', 'program', 'property', 'raise', 'record', 'repeat',
'resourcestring', 'set', 'shl', 'shr', 'string', 'then', 'threadvar',
'to', 'try', 'type', 'unit', 'until', 'uses', 'var', 'while', 'with',
'xor', 'on'
]
DIRECTIVES = [
'absolute', 'abstract', 'assembler', 'at', 'automated', 'cdecl',
'contains', 'deprecated', 'dispid', 'dynamic', 'export',
'external', 'far', 'forward', 'implements', 'local',
'near', 'nodefault', 'on', 'overload', 'override',
'package', 'pascal', 'platform', 'private', 'protected', 'public',
'published', 'read', 'readonly', 'register', 'reintroduce',
'requires', 'resident', 'safecall', 'stdcall', 'stored', 'varargs',
'virtual', 'write', 'writeonly'
]
IDENT_KIND = CaseIgnoringWordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(DIRECTIVES, :directive)
NAME_FOLLOWS = CaseIgnoringWordList.new(false).
add(%w(procedure function .))
private
def scan_tokens tokens, options
state = :initial
last_token = ''
until eos?
kind = nil
match = nil
if state == :initial
if scan(/ \s+ /x)
tokens << [matched, :space]
next
elsif scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx)
tokens << [matched, :preprocessor]
next
elsif scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx)
tokens << [matched, :comment]
next
elsif match = scan(/ <[>=]? | >=? | :=? | [-+=*\/;,@\^|\(\)\[\]] | \.\. /x)
kind = :operator
elsif match = scan(/\./)
kind = :operator
if last_token == 'end'
tokens << [match, kind]
next
end
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
kind = NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match]
elsif match = scan(/ ' ( [^\n']|'' ) (?:'|$) /x)
tokens << [:open, :char]
tokens << ["'", :delimiter]
tokens << [self[1], :content]
tokens << ["'", :delimiter]
tokens << [:close, :char]
next
elsif match = scan(/ ' /x)
tokens << [:open, :string]
state = :string
kind = :delimiter
elsif scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x)
kind = :char
elsif scan(/ \$ [0-9A-Fa-f]+ /x)
kind = :hex
elsif scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x)
kind = :integer
elsif scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x)
kind = :float
else
kind = :error
getch
end
elsif state == :string
if scan(/[^\n']+/)
kind = :content
elsif scan(/''/)
kind = :char
elsif scan(/'/)
tokens << ["'", :delimiter]
tokens << [:close, :string]
state = :initial
next
elsif scan(/\n/)
tokens << [:close, :string]
kind = :error
state = :initial
else
raise "else case \' reached; %p not handled." % peek(1), tokens
end
else
raise 'else-case reached', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
end
raise_inspect 'Empty token', tokens unless match
last_token = match
tokens << [match, kind]
end
tokens
end
end
end
end

View File

@ -1,110 +0,0 @@
module CodeRay
module Scanners
class Diff < Scanner
register_for :diff
title 'diff output'
def scan_tokens tokens, options
line_kind = nil
state = :initial
until eos?
kind = match = nil
if match = scan(/\n/)
if line_kind
tokens << [:end_line, line_kind]
line_kind = nil
end
tokens << [match, :space]
next
end
case state
when :initial
if match = scan(/--- |\+\+\+ |=+|_+/)
tokens << [:begin_line, line_kind = :head]
tokens << [match, :head]
next unless match = scan(/.+/)
kind = :plain
elsif match = scan(/Index: |Property changes on: /)
tokens << [:begin_line, line_kind = :head]
tokens << [match, :head]
next unless match = scan(/.+/)
kind = :plain
elsif match = scan(/Added: /)
tokens << [:begin_line, line_kind = :head]
tokens << [match, :head]
next unless match = scan(/.+/)
kind = :plain
state = :added
elsif match = scan(/\\ /)
tokens << [:begin_line, line_kind = :change]
tokens << [match, :change]
next unless match = scan(/.+/)
kind = :plain
elsif match = scan(/@@(?>[^@\n]*)@@/)
if check(/\n|$/)
tokens << [:begin_line, line_kind = :change]
else
tokens << [:open, :change]
end
tokens << [match[0,2], :change]
tokens << [match[2...-2], :plain]
tokens << [match[-2,2], :change]
tokens << [:close, :change] unless line_kind
next unless match = scan(/.+/)
kind = :plain
elsif match = scan(/\+/)
tokens << [:begin_line, line_kind = :insert]
tokens << [match, :insert]
next unless match = scan(/.+/)
kind = :plain
elsif match = scan(/-/)
tokens << [:begin_line, line_kind = :delete]
tokens << [match, :delete]
next unless match = scan(/.+/)
kind = :plain
elsif scan(/ .*/)
kind = :comment
elsif scan(/.+/)
tokens << [:begin_line, line_kind = :comment]
kind = :plain
else
raise_inspect 'else case rached'
end
when :added
if match = scan(/ \+/)
tokens << [:begin_line, line_kind = :insert]
tokens << [match, :insert]
next unless match = scan(/.+/)
kind = :plain
else
state = :initial
next
end
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
tokens << [:end_line, line_kind] if line_kind
tokens
end
end
end
end

View File

@ -1,264 +0,0 @@
module CodeRay
module Scanners
load :java
class Groovy < Java
include Streamable
register_for :groovy
# TODO: Check this!
GROOVY_KEYWORDS = %w[
as assert def in
]
KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
case instanceof new return throw typeof while as assert in
]
GROOVY_MAGIC_VARIABLES = %w[ it ]
IDENT_KIND = Java::IDENT_KIND.dup.
add(GROOVY_KEYWORDS, :keyword).
add(GROOVY_MAGIC_VARIABLES, :local_variable)
ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # no 4-byte unicode chars? U[a-fA-F0-9]{8}
REGEXP_ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | \d | [bBdDsSwW\/] /x
# TODO: interpretation inside ', ", /
STRING_CONTENT_PATTERN = {
"'" => /(?>\\[^\\'\n]+|[^\\'\n]+)+/,
'"' => /[^\\$"\n]+/,
"'''" => /(?>[^\\']+|'(?!''))+/,
'"""' => /(?>[^\\$"]+|"(?!""))+/,
'/' => /[^\\$\/\n]+/,
}
def scan_tokens tokens, options
state = :initial
inline_block_stack = []
inline_block_paren_depth = nil
string_delimiter = nil
import_clause = class_name_follows = last_token = after_def = false
value_expected = true
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
tokens << [match, :space]
if match.index ?\n
import_clause = after_def = false
value_expected = true unless value_expected
end
next
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
value_expected = true
after_def = false
kind = :comment
elsif bol? && scan(/ \#!.* /x)
kind = :doctype
elsif import_clause && scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox)
after_def = value_expected = false
kind = :include
elsif match = scan(/ #{IDENT} | \[\] /ox)
kind = IDENT_KIND[match]
value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
if last_token == '.'
kind = :ident
elsif class_name_follows
kind = :class
class_name_follows = false
elsif after_def && check(/\s*[({]/)
kind = :method
after_def = false
elsif kind == :ident && last_token != '?' && check(/:/)
kind = :key
else
class_name_follows = true if match == 'class' || (import_clause && match == 'as')
import_clause = match == 'import'
after_def = true if match == 'def'
end
elsif scan(/;/)
import_clause = after_def = false
value_expected = true
kind = :operator
elsif scan(/\{/)
class_name_follows = after_def = false
value_expected = true
kind = :operator
if !inline_block_stack.empty?
inline_block_paren_depth += 1
end
# TODO: ~'...', ~"..." and ~/.../ style regexps
elsif match = scan(/ \.\.<? | \*?\.(?!\d)@? | \.& | \?:? | [,?:(\[] | -[->] | \+\+ |
&& | \|\| | \*\*=? | ==?~ | <=?>? | [-+*%^~&|>=!]=? | <<<?=? | >>>?=? /x)
value_expected = true
value_expected = :regexp if match == '~'
after_def = false
kind = :operator
elsif match = scan(/ [)\]}] /x)
value_expected = after_def = false
if !inline_block_stack.empty? && match == '}'
inline_block_paren_depth -= 1
if inline_block_paren_depth == 0 # closing brace of inline block reached
tokens << [match, :inline_delimiter]
tokens << [:close, :inline]
state, string_delimiter, inline_block_paren_depth = inline_block_stack.pop
next
end
end
kind = :operator
elsif check(/[\d.]/)
after_def = value_expected = false
if scan(/0[xX][0-9A-Fa-f]+/)
kind = :hex
elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
kind = :oct
elsif scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/)
kind = :float
elsif scan(/\d+[lLgG]?/)
kind = :integer
end
elsif match = scan(/'''|"""/)
after_def = value_expected = false
state = :multiline_string
tokens << [:open, :string]
string_delimiter = match
kind = :delimiter
# TODO: record.'name'
elsif match = scan(/["']/)
after_def = value_expected = false
state = match == '/' ? :regexp : :string
tokens << [:open, state]
string_delimiter = match
kind = :delimiter
elsif value_expected && (match = scan(/\//))
after_def = value_expected = false
tokens << [:open, :regexp]
state = :regexp
string_delimiter = '/'
kind = :delimiter
elsif scan(/ @ #{IDENT} /ox)
after_def = value_expected = false
kind = :annotation
elsif scan(/\//)
after_def = false
value_expected = true
kind = :operator
else
getch
kind = :error
end
when :string, :regexp, :multiline_string
if scan(STRING_CONTENT_PATTERN[string_delimiter])
kind = :content
elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/)
tokens << [match, :delimiter]
if state == :regexp
# TODO: regexp modifiers? s, m, x, i?
modifiers = scan(/[ix]+/)
tokens << [modifiers, :modifier] if modifiers && !modifiers.empty?
end
state = :string if state == :multiline_string
tokens << [:close, state]
string_delimiter = nil
after_def = value_expected = false
state = :initial
next
elsif (state == :string || state == :multiline_string) &&
(match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
if string_delimiter[0] == ?' && !(match == "\\\\" || match == "\\'")
kind = :content
else
kind = :char
end
elsif state == :regexp && scan(/ \\ (?: #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif match = scan(/ \$ #{IDENT} /mox)
tokens << [:open, :inline]
tokens << ['$', :inline_delimiter]
match = match[1..-1]
tokens << [match, IDENT_KIND[match]]
tokens << [:close, :inline]
next
elsif match = scan(/ \$ \{ /x)
tokens << [:open, :inline]
tokens << ['${', :inline_delimiter]
inline_block_stack << [state, string_delimiter, inline_block_paren_depth]
inline_block_paren_depth = 1
state = :initial
next
elsif scan(/ \$ /mx)
kind = :content
elsif scan(/ \\. /mx)
kind = :content
elsif scan(/ \\ | \n /x)
tokens << [:close, state]
kind = :error
after_def = value_expected = false
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
last_token = match unless [:space, :comment, :doctype].include? kind
tokens << [match, kind]
end
if [:multiline_string, :string, :regexp].include? state
tokens << [:close, state]
end
tokens
end
end
end
end

View File

@ -1,182 +0,0 @@
module CodeRay
module Scanners
# HTML Scanner
class HTML < Scanner
include Streamable
register_for :html
KINDS_NOT_LOC = [
:comment, :doctype, :preprocessor,
:tag, :attribute_name, :operator,
:attribute_value, :delimiter, :content,
:plain, :entity, :error
]
ATTR_NAME = /[\w.:-]+/
ATTR_VALUE_UNQUOTED = ATTR_NAME
TAG_END = /\/?>/
HEX = /[0-9a-fA-F]/
ENTITY = /
&
(?:
\w+
|
\#
(?:
\d+
|
x#{HEX}+
)
)
;
/ox
PLAIN_STRING_CONTENT = {
"'" => /[^&'>\n]+/,
'"' => /[^&">\n]+/,
}
def reset
super
@state = :initial
end
private
def setup
@state = :initial
@plain_string_content = nil
end
def scan_tokens tokens, options
state = @state
plain_string_content = @plain_string_content
until eos?
kind = nil
match = nil
if scan(/\s+/m)
kind = :space
else
case state
when :initial
if scan(/<!--.*?-->/m)
kind = :comment
elsif scan(/<!DOCTYPE.*?>/m)
kind = :doctype
elsif scan(/<\?xml.*?\?>/m)
kind = :preprocessor
elsif scan(/<\?.*?\?>|<%.*?%>/m)
kind = :comment
elsif scan(/<\/[-\w.:]*>/m)
kind = :tag
elsif match = scan(/<[-\w.:]+>?/m)
kind = :tag
state = :attribute unless match[-1] == ?>
elsif scan(/[^<>&]+/)
kind = :plain
elsif scan(/#{ENTITY}/ox)
kind = :entity
elsif scan(/[<>&]/)
kind = :error
else
raise_inspect '[BUG] else-case reached with state %p' % [state], tokens
end
when :attribute
if scan(/#{TAG_END}/o)
kind = :tag
state = :initial
elsif scan(/#{ATTR_NAME}/o)
kind = :attribute_name
state = :attribute_equal
else
kind = :error
getch
end
when :attribute_equal
if scan(/=/)
kind = :operator
state = :attribute_value
elsif scan(/#{ATTR_NAME}/o)
kind = :attribute_name
elsif scan(/#{TAG_END}/o)
kind = :tag
state = :initial
elsif scan(/./)
kind = :error
state = :attribute
end
when :attribute_value
if scan(/#{ATTR_VALUE_UNQUOTED}/o)
kind = :attribute_value
state = :attribute
elsif match = scan(/["']/)
tokens << [:open, :string]
state = :attribute_value_string
plain_string_content = PLAIN_STRING_CONTENT[match]
kind = :delimiter
elsif scan(/#{TAG_END}/o)
kind = :tag
state = :initial
else
kind = :error
getch
end
when :attribute_value_string
if scan(plain_string_content)
kind = :content
elsif scan(/['"]/)
tokens << [matched, :delimiter]
tokens << [:close, :string]
state = :attribute
next
elsif scan(/#{ENTITY}/ox)
kind = :entity
elsif scan(/&/)
kind = :content
elsif scan(/[\n>]/)
tokens << [:close, :string]
kind = :error
state = :initial
end
else
raise_inspect 'Unknown state: %p' % [state], tokens
end
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if options[:keep_state]
@state = state
@plain_string_content = plain_string_content
end
tokens
end
end
end
end

View File

@ -1,176 +0,0 @@
module CodeRay
module Scanners
class Java < Scanner
include Streamable
register_for :java
helper :builtin_types
# http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
KEYWORDS = %w[
assert break case catch continue default do else
finally for if instanceof import new package
return switch throw try typeof while
debugger export
]
RESERVED = %w[ const goto ]
CONSTANTS = %w[ false null true ]
MAGIC_VARIABLES = %w[ this super ]
TYPES = %w[
boolean byte char class double enum float int interface long
short void
] << '[]' # because int[] should be highlighted as a type
DIRECTIVES = %w[
abstract extends final implements native private protected public
static strictfp synchronized throws transient volatile
]
IDENT_KIND = WordList.new(:ident).
add(KEYWORDS, :keyword).
add(RESERVED, :reserved).
add(CONSTANTS, :pre_constant).
add(MAGIC_VARIABLES, :local_variable).
add(TYPES, :type).
add(BuiltinTypes::List, :pre_type).
add(BuiltinTypes::List.select { |builtin| builtin[/(Error|Exception)$/] }, :exception).
add(DIRECTIVES, :directive)
ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
STRING_CONTENT_PATTERN = {
"'" => /[^\\']+/,
'"' => /[^\\"]+/,
'/' => /[^\\\/]+/,
}
IDENT = /[a-zA-Z_][A-Za-z_0-9]*/
def scan_tokens tokens, options
state = :initial
string_delimiter = nil
import_clause = class_name_follows = last_token_dot = false
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
tokens << [match, :space]
next
elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
tokens << [match, :comment]
next
elsif import_clause && scan(/ #{IDENT} (?: \. #{IDENT} )* /ox)
kind = :include
elsif match = scan(/ #{IDENT} | \[\] /ox)
kind = IDENT_KIND[match]
if last_token_dot
kind = :ident
elsif class_name_follows
kind = :class
class_name_follows = false
else
import_clause = true if match == 'import'
class_name_follows = true if match == 'class' || match == 'interface'
end
elsif scan(/ \.(?!\d) | [,?:()\[\]}] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? | <<<?=? | >>>?=? /x)
kind = :operator
elsif scan(/;/)
import_clause = false
kind = :operator
elsif scan(/\{/)
class_name_follows = false
kind = :operator
elsif check(/[\d.]/)
if scan(/0[xX][0-9A-Fa-f]+/)
kind = :hex
elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
kind = :oct
elsif scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/)
kind = :float
elsif scan(/\d+[lL]?/)
kind = :integer
end
elsif match = scan(/["']/)
tokens << [:open, :string]
state = :string
string_delimiter = match
kind = :delimiter
elsif scan(/ @ #{IDENT} /ox)
kind = :annotation
else
getch
kind = :error
end
when :string
if scan(STRING_CONTENT_PATTERN[string_delimiter])
kind = :content
elsif match = scan(/["'\/]/)
tokens << [match, :delimiter]
tokens << [:close, state]
string_delimiter = nil
state = :initial
next
elsif state == :string && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
if string_delimiter == "'" && !(match == "\\\\" || match == "\\'")
kind = :content
else
kind = :char
end
elsif scan(/\\./m)
kind = :content
elsif scan(/ \\ | $ /x)
tokens << [:close, state]
kind = :error
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
last_token_dot = match == '.'
tokens << [match, kind]
end
if state == :string
tokens << [:close, state]
end
tokens
end
end
end
end

View File

@ -1,224 +0,0 @@
module CodeRay
module Scanners
class JavaScript < Scanner
include Streamable
register_for :java_script
file_extension 'js'
# The actual JavaScript keywords.
KEYWORDS = %w[
break case catch continue default delete do else
finally for function if in instanceof new
return switch throw try typeof var void while with
]
PREDEFINED_CONSTANTS = %w[
false null true undefined
]
MAGIC_VARIABLES = %w[ this arguments ] # arguments was introduced in JavaScript 1.4
KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
case delete in instanceof new return throw typeof with
]
# Reserved for future use.
RESERVED_WORDS = %w[
abstract boolean byte char class debugger double enum export extends
final float goto implements import int interface long native package
private protected public short static super synchronized throws transient
volatile
]
IDENT_KIND = WordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_CONSTANTS, :pre_constant).
add(MAGIC_VARIABLES, :local_variable).
add(KEYWORDS, :keyword)
ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
REGEXP_ESCAPE = / [bBdDsSwW] /x
STRING_CONTENT_PATTERN = {
"'" => /[^\\']+/,
'"' => /[^\\"]+/,
'/' => /[^\\\/]+/,
}
KEY_CHECK_PATTERN = {
"'" => / [^\\']* (?: \\.? [^\\']* )* '? \s* : /x,
'"' => / [^\\"]* (?: \\.? [^\\"]* )* "? \s* : /x,
}
def scan_tokens tokens, options
state = :initial
string_delimiter = nil
value_expected = true
key_expected = false
function_expected = false
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
value_expected = true if !value_expected && match.index(?\n)
tokens << [match, :space]
next
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
value_expected = true
kind = :comment
elsif check(/\.?\d/)
key_expected = value_expected = false
if scan(/0[xX][0-9A-Fa-f]+/)
kind = :hex
elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
kind = :oct
elsif scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
kind = :float
elsif scan(/\d+/)
kind = :integer
end
elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim)
# FIXME: scan over nested tags
xml_scanner.tokenize match
value_expected = false
next
elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x)
value_expected = true
last_operator = match[-1]
key_expected = (last_operator == ?{) || (last_operator == ?,)
function_expected = false
kind = :operator
elsif scan(/ [)\]}]+ /x)
function_expected = key_expected = value_expected = false
kind = :operator
elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x)
kind = IDENT_KIND[match]
value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
# TODO: labels
if kind == :ident
if match.index(?$) # $ allowed inside an identifier
kind = :predefined
elsif function_expected
kind = :function
elsif check(/\s*[=:]\s*function\b/)
kind = :function
elsif key_expected && check(/\s*:/)
kind = :key
end
end
function_expected = (kind == :keyword) && (match == 'function')
key_expected = false
elsif match = scan(/["']/)
if key_expected && check(KEY_CHECK_PATTERN[match])
state = :key
else
state = :string
end
tokens << [:open, state]
string_delimiter = match
kind = :delimiter
elsif value_expected && (match = scan(/\/(?=\S)/))
tokens << [:open, :regexp]
state = :regexp
string_delimiter = '/'
kind = :delimiter
elsif scan(/ \/ /x)
value_expected = true
key_expected = false
kind = :operator
else
getch
kind = :error
end
when :string, :regexp, :key
if scan(STRING_CONTENT_PATTERN[string_delimiter])
kind = :content
elsif match = scan(/["'\/]/)
tokens << [match, :delimiter]
if state == :regexp
modifiers = scan(/[gim]+/)
tokens << [modifiers, :modifier] if modifiers && !modifiers.empty?
end
tokens << [:close, state]
string_delimiter = nil
key_expected = value_expected = false
state = :initial
next
elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
if string_delimiter == "'" && !(match == "\\\\" || match == "\\'")
kind = :content
else
kind = :char
end
elsif state == :regexp && scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif scan(/\\./m)
kind = :content
elsif scan(/ \\ | $ /x)
tokens << [:close, state]
kind = :error
key_expected = value_expected = false
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if [:string, :regexp].include? state
tokens << [:close, state]
end
tokens
end
protected
def reset_instance
super
@xml_scanner.reset if defined? @xml_scanner
end
def xml_scanner
@xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false
end
end
end
end

View File

@ -1,224 +0,0 @@
module CodeRay
module Scanners
class JavaScript < Scanner
include Streamable
register_for :java_script
file_extension 'js'
# The actual JavaScript keywords.
KEYWORDS = %w[
break case catch continue default delete do else
finally for function if in instanceof new
return switch throw try typeof var void while with
]
PREDEFINED_CONSTANTS = %w[
false null true undefined
]
MAGIC_VARIABLES = %w[ this arguments ] # arguments was introduced in JavaScript 1.4
KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
case delete in instanceof new return throw typeof with
]
# Reserved for future use.
RESERVED_WORDS = %w[
abstract boolean byte char class debugger double enum export extends
final float goto implements import int interface long native package
private protected public short static super synchronized throws transient
volatile
]
IDENT_KIND = WordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_CONSTANTS, :pre_constant).
add(MAGIC_VARIABLES, :local_variable).
add(KEYWORDS, :keyword)
ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
REGEXP_ESCAPE = / [bBdDsSwW] /x
STRING_CONTENT_PATTERN = {
"'" => /[^\\']+/,
'"' => /[^\\"]+/,
'/' => /[^\\\/]+/,
}
KEY_CHECK_PATTERN = {
"'" => / (?> [^\\']* (?: \\. [^\\']* )* ) ' \s* : /mx,
'"' => / (?> [^\\"]* (?: \\. [^\\"]* )* ) " \s* : /mx,
}
def scan_tokens tokens, options
state = :initial
string_delimiter = nil
value_expected = true
key_expected = false
function_expected = false
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
value_expected = true if !value_expected && match.index(?\n)
tokens << [match, :space]
next
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
value_expected = true
kind = :comment
elsif check(/\.?\d/)
key_expected = value_expected = false
if scan(/0[xX][0-9A-Fa-f]+/)
kind = :hex
elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
kind = :oct
elsif scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
kind = :float
elsif scan(/\d+/)
kind = :integer
end
elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim)
# FIXME: scan over nested tags
xml_scanner.tokenize match
value_expected = false
next
elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x)
value_expected = true
last_operator = match[-1]
key_expected = (last_operator == ?{) || (last_operator == ?,)
function_expected = false
kind = :operator
elsif scan(/ [)\]}]+ /x)
function_expected = key_expected = value_expected = false
kind = :operator
elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x)
kind = IDENT_KIND[match]
value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
# TODO: labels
if kind == :ident
if match.index(?$) # $ allowed inside an identifier
kind = :predefined
elsif function_expected
kind = :function
elsif check(/\s*[=:]\s*function\b/)
kind = :function
elsif key_expected && check(/\s*:/)
kind = :key
end
end
function_expected = (kind == :keyword) && (match == 'function')
key_expected = false
elsif match = scan(/["']/)
if key_expected && check(KEY_CHECK_PATTERN[match])
state = :key
else
state = :string
end
tokens << [:open, state]
string_delimiter = match
kind = :delimiter
elsif value_expected && (match = scan(/\/(?=\S)/))
tokens << [:open, :regexp]
state = :regexp
string_delimiter = '/'
kind = :delimiter
elsif scan(/ \/ /x)
value_expected = true
key_expected = false
kind = :operator
else
getch
kind = :error
end
when :string, :regexp, :key
if scan(STRING_CONTENT_PATTERN[string_delimiter])
kind = :content
elsif match = scan(/["'\/]/)
tokens << [match, :delimiter]
if state == :regexp
modifiers = scan(/[gim]+/)
tokens << [modifiers, :modifier] if modifiers && !modifiers.empty?
end
tokens << [:close, state]
string_delimiter = nil
key_expected = value_expected = false
state = :initial
next
elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
if string_delimiter == "'" && !(match == "\\\\" || match == "\\'")
kind = :content
else
kind = :char
end
elsif state == :regexp && scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif scan(/\\./m)
kind = :content
elsif scan(/ \\ | $ /x)
tokens << [:close, state]
kind = :error
key_expected = value_expected = false
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if [:string, :regexp].include? state
tokens << [:close, state]
end
tokens
end
protected
def reset_instance
super
@xml_scanner.reset if defined? @xml_scanner
end
def xml_scanner
@xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false
end
end
end
end

View File

@ -1,108 +0,0 @@
module CodeRay
module Scanners
class JSON < Scanner
include Streamable
register_for :json
file_extension 'json'
KINDS_NOT_LOC = [
:float, :char, :content, :delimiter,
:error, :integer, :operator, :value,
]
ESCAPE = / [bfnrt\\"\/] /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x
def scan_tokens tokens, options
state = :initial
stack = []
key_expected = false
until eos?
kind = nil
match = nil
case state
when :initial
if match = scan(/ \s+ | \\\n /x)
tokens << [match, :space]
next
elsif match = scan(/ [:,\[{\]}] /x)
kind = :operator
case match
when '{' then stack << :object; key_expected = true
when '[' then stack << :array
when ':' then key_expected = false
when ',' then key_expected = true if stack.last == :object
when '}', ']' then stack.pop # no error recovery, but works for valid JSON
end
elsif match = scan(/ true | false | null /x)
kind = :value
elsif match = scan(/-?(?:0|[1-9]\d*)/)
kind = :integer
if scan(/\.\d+(?:[eE][-+]?\d+)?|[eE][-+]?\d+/)
match << matched
kind = :float
end
elsif match = scan(/"/)
state = key_expected ? :key : :string
tokens << [:open, state]
kind = :delimiter
else
getch
kind = :error
end
when :string, :key
if scan(/[^\\"]+/)
kind = :content
elsif scan(/"/)
tokens << ['"', :delimiter]
tokens << [:close, state]
state = :initial
next
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif scan(/\\./m)
kind = :content
elsif scan(/ \\ | $ /x)
tokens << [:close, state]
kind = :error
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if [:string, :key].include? state
tokens << [:close, state]
end
tokens
end
end
end
end

View File

@ -1,136 +0,0 @@
module CodeRay
module Scanners
load :html
load :ruby
# Nitro XHTML Scanner
class NitroXHTML < Scanner
include Streamable
register_for :nitro_xhtml
file_extension :xhtml
title 'Nitro XHTML'
KINDS_NOT_LOC = HTML::KINDS_NOT_LOC
NITRO_RUBY_BLOCK = /
<\?r
(?>
[^\?]*
(?> \?(?!>) [^\?]* )*
)
(?: \?> )?
|
<ruby>
(?>
[^<]*
(?> <(?!\/ruby>) [^<]* )*
)
(?: <\/ruby> )?
|
<%
(?>
[^%]*
(?> %(?!>) [^%]* )*
)
(?: %> )?
/mx
NITRO_VALUE_BLOCK = /
\#
(?:
\{
[^{}]*
(?>
\{ [^}]* \}
(?> [^{}]* )
)*
\}?
| \| [^|]* \|?
| \( [^)]* \)?
| \[ [^\]]* \]?
| \\ [^\\]* \\?
)
/x
NITRO_ENTITY = /
% (?: \#\d+ | \w+ ) ;
/
START_OF_RUBY = /
(?=[<\#%])
< (?: \?r | % | ruby> )
| \# [{(|]
| % (?: \#\d+ | \w+ ) ;
/x
CLOSING_PAREN = Hash.new do |h, p|
h[p] = p
end.update( {
'(' => ')',
'[' => ']',
'{' => '}',
} )
private
def setup
@ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true
@html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true
end
def reset_instance
super
@html_scanner.reset
end
def scan_tokens tokens, options
until eos?
if (match = scan_until(/(?=#{START_OF_RUBY})/o) || scan_until(/\z/)) and not match.empty?
@html_scanner.tokenize match
elsif match = scan(/#{NITRO_VALUE_BLOCK}/o)
start_tag = match[0,2]
delimiter = CLOSING_PAREN[start_tag[1,1]]
end_tag = match[-1,1] == delimiter ? delimiter : ''
tokens << [:open, :inline]
tokens << [start_tag, :inline_delimiter]
code = match[start_tag.size .. -1 - end_tag.size]
@ruby_scanner.tokenize code
tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
tokens << [:close, :inline]
elsif match = scan(/#{NITRO_RUBY_BLOCK}/o)
start_tag = '<?r'
end_tag = match[-2,2] == '?>' ? '?>' : ''
tokens << [:open, :inline]
tokens << [start_tag, :inline_delimiter]
code = match[start_tag.size .. -(end_tag.size)-1]
@ruby_scanner.tokenize code
tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
tokens << [:close, :inline]
elsif entity = scan(/#{NITRO_ENTITY}/o)
tokens << [entity, :entity]
elsif scan(/%/)
tokens << [matched, :error]
else
raise_inspect 'else-case reached!', tokens
end
end
tokens
end
end
end
end

View File

@ -1,533 +0,0 @@
module CodeRay
module Scanners
load :html
# Original by Stefan Walk.
class PHP < Scanner
register_for :php
file_extension 'php'
KINDS_NOT_LOC = HTML::KINDS_NOT_LOC
def setup
@html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true
end
def reset_instance
super
@html_scanner.reset
end
module Words
# according to http://www.php.net/manual/en/reserved.keywords.php
KEYWORDS = %w[
abstract and array as break case catch class clone const continue declare default do else elseif
enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global
goto if implements interface instanceof namespace new or private protected public static switch
throw try use var while xor
cfunction old_function
]
TYPES = %w[ int integer float double bool boolean string array object resource ]
LANGUAGE_CONSTRUCTS = %w[
die echo empty exit eval include include_once isset list
require require_once return print unset
]
CLASSES = %w[ Directory stdClass __PHP_Incomplete_Class exception php_user_filter Closure ]
# according to http://php.net/quickref.php on 2009-04-21;
# all functions with _ excluded (module functions) and selected additional functions
BUILTIN_FUNCTIONS = %w[
abs acos acosh addcslashes addslashes aggregate array arsort ascii2ebcdic asin asinh asort assert atan atan2
atanh basename bcadd bccomp bcdiv bcmod bcmul bcpow bcpowmod bcscale bcsqrt bcsub bin2hex bindec
bindtextdomain bzclose bzcompress bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite
calculhmac ceil chdir checkdate checkdnsrr chgrp chmod chop chown chr chroot clearstatcache closedir closelog
compact constant copy cos cosh count crc32 crypt current date dcgettext dcngettext deaggregate decbin dechex
decoct define defined deg2rad delete dgettext die dirname diskfreespace dl dngettext doubleval each
ebcdic2ascii echo empty end ereg eregi escapeshellarg escapeshellcmd eval exec exit exp explode expm1 extract
fclose feof fflush fgetc fgetcsv fgets fgetss file fileatime filectime filegroup fileinode filemtime fileowner
fileperms filepro filesize filetype floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv
fputs fread frenchtojd fscanf fseek fsockopen fstat ftell ftok ftruncate fwrite getallheaders getcwd getdate
getenv gethostbyaddr gethostbyname gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid
getmyuid getopt getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext
gettimeofday gettype glob gmdate gmmktime gmstrftime gregoriantojd gzclose gzcompress gzdecode gzdeflate
gzencode gzeof gzfile gzgetc gzgets gzgetss gzinflate gzopen gzpassthru gzputs gzread gzrewind gzseek gztell
gzuncompress gzwrite hash header hebrev hebrevc hexdec htmlentities htmlspecialchars hypot iconv idate
implode include intval ip2long iptcembed iptcparse isset
jddayofweek jdmonthname jdtofrench jdtogregorian jdtojewish jdtojulian jdtounix jewishtojd join jpeg2wbmp
juliantojd key krsort ksort lcfirst lchgrp lchown levenshtein link linkinfo list localeconv localtime log
log10 log1p long2ip lstat ltrim mail main max md5 metaphone mhash microtime min mkdir mktime msql natcasesort
natsort next ngettext nl2br nthmac octdec opendir openlog
ord overload pack passthru pathinfo pclose pfsockopen phpcredits phpinfo phpversion pi png2wbmp popen pos pow
prev print printf putenv quotemeta rad2deg rand range rawurldecode rawurlencode readdir readfile readgzfile
readline readlink realpath recode rename require reset rewind rewinddir rmdir round rsort rtrim scandir
serialize setcookie setlocale setrawcookie settype sha1 shuffle signeurlpaiement sin sinh sizeof sleep snmpget
snmpgetnext snmprealwalk snmpset snmpwalk snmpwalkoid sort soundex split spliti sprintf sqrt srand sscanf stat
strcasecmp strchr strcmp strcoll strcspn strftime stripcslashes stripos stripslashes stristr strlen
strnatcasecmp strnatcmp strncasecmp strncmp strpbrk strpos strptime strrchr strrev strripos strrpos strspn
strstr strtok strtolower strtotime strtoupper strtr strval substr symlink syslog system tan tanh tempnam
textdomain time tmpfile touch trim uasort ucfirst ucwords uksort umask uniqid unixtojd unlink unpack
unserialize unset urldecode urlencode usleep usort vfprintf virtual vprintf vsprintf wordwrap
array_change_key_case array_chunk array_combine array_count_values array_diff array_diff_assoc
array_diff_key array_diff_uassoc array_diff_ukey array_fill array_fill_keys array_filter array_flip
array_intersect array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey
array_key_exists array_keys array_map array_merge array_merge_recursive array_multisort array_pad
array_pop array_product array_push array_rand array_reduce array_reverse array_search array_shift
array_slice array_splice array_sum array_udiff array_udiff_assoc array_udiff_uassoc array_uintersect
array_uintersect_assoc array_uintersect_uassoc array_unique array_unshift array_values array_walk
array_walk_recursive
assert_options base_convert base64_decode base64_encode
chunk_split class_exists class_implements class_parents
count_chars debug_backtrace debug_print_backtrace debug_zval_dump
error_get_last error_log error_reporting extension_loaded
file_exists file_get_contents file_put_contents load_file
func_get_arg func_get_args func_num_args function_exists
get_browser get_called_class get_cfg_var get_class get_class_methods get_class_vars
get_current_user get_declared_classes get_declared_interfaces get_defined_constants
get_defined_functions get_defined_vars get_extension_funcs get_headers get_html_translation_table
get_include_path get_included_files get_loaded_extensions get_magic_quotes_gpc get_magic_quotes_runtime
get_meta_tags get_object_vars get_parent_class get_required_filesget_resource_type
gc_collect_cycles gc_disable gc_enable gc_enabled
halt_compiler headers_list headers_sent highlight_file highlight_string
html_entity_decode htmlspecialchars_decode
in_array include_once inclued_get_data
is_a is_array is_binary is_bool is_buffer is_callable is_dir is_double is_executable is_file is_finite
is_float is_infinite is_int is_integer is_link is_long is_nan is_null is_numeric is_object is_readable
is_real is_resource is_scalar is_soap_fault is_string is_subclass_of is_unicode is_uploaded_file
is_writable is_writeable
locale_get_default locale_set_default
number_format override_function parse_str parse_url
php_check_syntax php_ini_loaded_file php_ini_scanned_files php_logo_guid php_sapi_name
php_strip_whitespace php_uname
preg_filter preg_grep preg_last_error preg_match preg_match_all preg_quote preg_replace
preg_replace_callback preg_split print_r
require_once register_shutdown_function register_tick_function
set_error_handler set_exception_handler set_file_buffer set_include_path
set_magic_quotes_runtime set_time_limit shell_exec
str_getcsv str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split str_word_count
strip_tags substr_compare substr_count substr_replace
time_nanosleep time_sleep_until
token_get_all token_name trigger_error
unregister_tick_function use_soap_error_handler user_error
utf8_decode utf8_encode var_dump var_export
version_compare
zend_logo_guid zend_thread_id zend_version
create_function call_user_func_array
posix_access posix_ctermid posix_get_last_error posix_getcwd posix_getegid
posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups
posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid
posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid
posix_initgroups posix_isatty posix_kill posix_mkfifo posix_mknod
posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid
posix_setuid posix_strerror posix_times posix_ttyname posix_uname
pcntl_alarm pcntl_exec pcntl_fork pcntl_getpriority pcntl_setpriority
pcntl_signal pcntl_signal_dispatch pcntl_sigprocmask pcntl_sigtimedwait
pcntl_sigwaitinfo pcntl_wait pcntl_waitpid pcntl_wexitstatus pcntl_wifexited
pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig
]
# TODO: more built-in PHP functions?
EXCEPTIONS = %w[
E_ERROR E_WARNING E_PARSE E_NOTICE E_CORE_ERROR E_CORE_WARNING E_COMPILE_ERROR E_COMPILE_WARNING
E_USER_ERROR E_USER_WARNING E_USER_NOTICE E_DEPRECATED E_USER_DEPRECATED E_ALL E_STRICT
]
CONSTANTS = %w[
null true false self parent
__LINE__ __DIR__ __FILE__ __LINE__
__CLASS__ __NAMESPACE__ __METHOD__ __FUNCTION__
PHP_VERSION PHP_MAJOR_VERSION PHP_MINOR_VERSION PHP_RELEASE_VERSION PHP_VERSION_ID PHP_EXTRA_VERSION PHP_ZTS
PHP_DEBUG PHP_MAXPATHLEN PHP_OS PHP_SAPI PHP_EOL PHP_INT_MAX PHP_INT_SIZE DEFAULT_INCLUDE_PATH
PEAR_INSTALL_DIR PEAR_EXTENSION_DIR PHP_EXTENSION_DIR PHP_PREFIX PHP_BINDIR PHP_LIBDIR PHP_DATADIR
PHP_SYSCONFDIR PHP_LOCALSTATEDIR PHP_CONFIG_FILE_PATH PHP_CONFIG_FILE_SCAN_DIR PHP_SHLIB_SUFFIX
PHP_OUTPUT_HANDLER_START PHP_OUTPUT_HANDLER_CONT PHP_OUTPUT_HANDLER_END
__COMPILER_HALT_OFFSET__
EXTR_OVERWRITE EXTR_SKIP EXTR_PREFIX_SAME EXTR_PREFIX_ALL EXTR_PREFIX_INVALID EXTR_PREFIX_IF_EXISTS
EXTR_IF_EXISTS SORT_ASC SORT_DESC SORT_REGULAR SORT_NUMERIC SORT_STRING CASE_LOWER CASE_UPPER COUNT_NORMAL
COUNT_RECURSIVE ASSERT_ACTIVE ASSERT_CALLBACK ASSERT_BAIL ASSERT_WARNING ASSERT_QUIET_EVAL CONNECTION_ABORTED
CONNECTION_NORMAL CONNECTION_TIMEOUT INI_USER INI_PERDIR INI_SYSTEM INI_ALL M_E M_LOG2E M_LOG10E M_LN2 M_LN10
M_PI M_PI_2 M_PI_4 M_1_PI M_2_PI M_2_SQRTPI M_SQRT2 M_SQRT1_2 CRYPT_SALT_LENGTH CRYPT_STD_DES CRYPT_EXT_DES
CRYPT_MD5 CRYPT_BLOWFISH DIRECTORY_SEPARATOR SEEK_SET SEEK_CUR SEEK_END LOCK_SH LOCK_EX LOCK_UN LOCK_NB
HTML_SPECIALCHARS HTML_ENTITIES ENT_COMPAT ENT_QUOTES ENT_NOQUOTES INFO_GENERAL INFO_CREDITS
INFO_CONFIGURATION INFO_MODULES INFO_ENVIRONMENT INFO_VARIABLES INFO_LICENSE INFO_ALL CREDITS_GROUP
CREDITS_GENERAL CREDITS_SAPI CREDITS_MODULES CREDITS_DOCS CREDITS_FULLPAGE CREDITS_QA CREDITS_ALL STR_PAD_LEFT
STR_PAD_RIGHT STR_PAD_BOTH PATHINFO_DIRNAME PATHINFO_BASENAME PATHINFO_EXTENSION PATH_SEPARATOR CHAR_MAX
LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_ALL LC_MESSAGES ABDAY_1 ABDAY_2 ABDAY_3 ABDAY_4 ABDAY_5
ABDAY_6 ABDAY_7 DAY_1 DAY_2 DAY_3 DAY_4 DAY_5 DAY_6 DAY_7 ABMON_1 ABMON_2 ABMON_3 ABMON_4 ABMON_5 ABMON_6
ABMON_7 ABMON_8 ABMON_9 ABMON_10 ABMON_11 ABMON_12 MON_1 MON_2 MON_3 MON_4 MON_5 MON_6 MON_7 MON_8 MON_9
MON_10 MON_11 MON_12 AM_STR PM_STR D_T_FMT D_FMT T_FMT T_FMT_AMPM ERA ERA_YEAR ERA_D_T_FMT ERA_D_FMT ERA_T_FMT
ALT_DIGITS INT_CURR_SYMBOL CURRENCY_SYMBOL CRNCYSTR MON_DECIMAL_POINT MON_THOUSANDS_SEP MON_GROUPING
POSITIVE_SIGN NEGATIVE_SIGN INT_FRAC_DIGITS FRAC_DIGITS P_CS_PRECEDES P_SEP_BY_SPACE N_CS_PRECEDES
N_SEP_BY_SPACE P_SIGN_POSN N_SIGN_POSN DECIMAL_POINT RADIXCHAR THOUSANDS_SEP THOUSEP GROUPING YESEXPR NOEXPR
YESSTR NOSTR CODESET LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG LOG_KERN
LOG_USER LOG_MAIL LOG_DAEMON LOG_AUTH LOG_SYSLOG LOG_LPR LOG_NEWS LOG_UUCP LOG_CRON LOG_AUTHPRIV LOG_LOCAL0
LOG_LOCAL1 LOG_LOCAL2 LOG_LOCAL3 LOG_LOCAL4 LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 LOG_PID LOG_CONS LOG_ODELAY
LOG_NDELAY LOG_NOWAIT LOG_PERROR
]
PREDEFINED = %w[
$GLOBALS $_SERVER $_GET $_POST $_FILES $_REQUEST $_SESSION $_ENV
$_COOKIE $php_errormsg $HTTP_RAW_POST_DATA $http_response_header
$argc $argv
]
IDENT_KIND = CaseIgnoringWordList.new(:ident).
add(KEYWORDS, :reserved).
add(TYPES, :pre_type).
add(LANGUAGE_CONSTRUCTS, :reserved).
add(BUILTIN_FUNCTIONS, :predefined).
add(CLASSES, :pre_constant).
add(EXCEPTIONS, :exception).
add(CONSTANTS, :pre_constant)
VARIABLE_KIND = WordList.new(:local_variable).
add(PREDEFINED, :predefined)
end
module RE
PHP_START = /
<script\s+[^>]*?language\s*=\s*"php"[^>]*?> |
<script\s+[^>]*?language\s*=\s*'php'[^>]*?> |
<\?php\d? |
<\?(?!xml)
/xi
PHP_END = %r!
</script> |
\?>
!xi
HTML_INDICATOR = /<!DOCTYPE html|<(?:html|body|div|p)[> ]/i
IDENTIFIER = /[a-z_\x7f-\xFF][a-z0-9_\x7f-\xFF]*/i
VARIABLE = /\$#{IDENTIFIER}/
OPERATOR = /
\.(?!\d)=? | # dot that is not decimal point, string concatenation
&& | \|\| | # logic
:: | -> | => | # scope, member, dictionary
\\(?!\n) | # namespace
\+\+ | -- | # increment, decrement
[,;?:()\[\]{}] | # simple delimiters
[-+*\/%&|^]=? | # ordinary math, binary logic, assignment shortcuts
[~$] | # whatever
=& | # reference assignment
[=!]=?=? | <> | # comparison and assignment
<<=? | >>=? | [<>]=? # comparison and shift
/x
end
def scan_tokens tokens, options
if string.respond_to?(:encoding)
unless string.encoding == Encoding::ASCII_8BIT
self.string = string.encode Encoding::ASCII_8BIT,
:invalid => :replace, :undef => :replace, :replace => '?'
end
end
if check(RE::PHP_START) || # starts with <?
(match?(/\s*<\S/) && exist?(RE::PHP_START)) || # starts with tag and contains <?
exist?(RE::HTML_INDICATOR) ||
check(/.{1,100}#{RE::PHP_START}/om) # PHP start after max 100 chars
# is HTML with embedded PHP, so start with HTML
states = [:initial]
else
# is just PHP, so start with PHP surrounded by HTML
states = [:initial, :php]
end
label_expected = true
case_expected = false
heredoc_delimiter = nil
delimiter = nil
modifier = nil
until eos?
match = nil
kind = nil
case states.last
when :initial # HTML
if scan RE::PHP_START
kind = :inline_delimiter
label_expected = true
states << :php
else
match = scan_until(/(?=#{RE::PHP_START})/o) || scan_until(/\z/)
@html_scanner.tokenize match unless match.empty?
next
end
when :php
if match = scan(/\s+/)
tokens << [match, :space]
next
elsif scan(%r! (?m: \/\* (?: .*? \*\/ | .* ) ) | (?://|\#) .*? (?=#{RE::PHP_END}|$) !xo)
kind = :comment
elsif match = scan(RE::IDENTIFIER)
kind = Words::IDENT_KIND[match]
if kind == :ident && label_expected && check(/:(?!:)/)
kind = :label
label_expected = true
else
label_expected = false
if kind == :ident && match =~ /^[A-Z]/
kind = :constant
elsif kind == :reserved
case match
when 'class'
states << :class_expected
when 'function'
states << :function_expected
when 'case', 'default'
case_expected = true
end
elsif match == 'b' && check(/['"]/) # binary string literal
modifier = match
next
end
end
elsif scan(/(?:\d+\.\d*|\d*\.\d+)(?:e[-+]?\d+)?|\d+e[-+]?\d+/i)
label_expected = false
kind = :float
elsif scan(/0x[0-9a-fA-F]+/)
label_expected = false
kind = :hex
elsif scan(/\d+/)
label_expected = false
kind = :integer
elsif scan(/'/)
tokens << [:open, :string]
if modifier
tokens << [modifier, :modifier]
modifier = nil
end
kind = :delimiter
states.push :sqstring
elsif match = scan(/["`]/)
tokens << [:open, :string]
if modifier
tokens << [modifier, :modifier]
modifier = nil
end
delimiter = match
kind = :delimiter
states.push :dqstring
elsif match = scan(RE::VARIABLE)
label_expected = false
kind = Words::VARIABLE_KIND[match]
elsif scan(/\{/)
kind = :operator
label_expected = true
states.push :php
elsif scan(/\}/)
if states.size == 1
kind = :error
else
states.pop
if states.last.is_a?(::Array)
delimiter = states.last[1]
states[-1] = states.last[0]
tokens << [matched, :delimiter]
tokens << [:close, :inline]
next
else
kind = :operator
label_expected = true
end
end
elsif scan(/@/)
label_expected = false
kind = :exception
elsif scan RE::PHP_END
kind = :inline_delimiter
states = [:initial]
elsif match = scan(/<<<(?:(#{RE::IDENTIFIER})|"(#{RE::IDENTIFIER})"|'(#{RE::IDENTIFIER})')/o)
tokens << [:open, :string]
warn 'heredoc in heredoc?' if heredoc_delimiter
heredoc_delimiter = Regexp.escape(self[1] || self[2] || self[3])
kind = :delimiter
states.push self[3] ? :sqstring : :dqstring
heredoc_delimiter = /#{heredoc_delimiter}(?=;?$)/
elsif match = scan(/#{RE::OPERATOR}/o)
label_expected = match == ';'
if case_expected
label_expected = true if match == ':'
case_expected = false
end
kind = :operator
else
getch
kind = :error
end
when :sqstring
if scan(heredoc_delimiter ? /[^\\\n]+/ : /[^'\\]+/)
kind = :content
elsif !heredoc_delimiter && scan(/'/)
tokens << [matched, :delimiter]
tokens << [:close, :string]
delimiter = nil
label_expected = false
states.pop
next
elsif heredoc_delimiter && match = scan(/\n/)
kind = :content
if scan heredoc_delimiter
tokens << ["\n", :content]
tokens << [matched, :delimiter]
tokens << [:close, :string]
heredoc_delimiter = nil
label_expected = false
states.pop
next
end
elsif scan(heredoc_delimiter ? /\\\\/ : /\\[\\'\n]/)
kind = :char
elsif scan(/\\./m)
kind = :content
elsif scan(/\\/)
kind = :error
end
when :dqstring
if scan(heredoc_delimiter ? /[^${\\\n]+/ : (delimiter == '"' ? /[^"${\\]+/ : /[^`${\\]+/))
kind = :content
elsif !heredoc_delimiter && scan(delimiter == '"' ? /"/ : /`/)
tokens << [matched, :delimiter]
tokens << [:close, :string]
delimiter = nil
label_expected = false
states.pop
next
elsif heredoc_delimiter && match = scan(/\n/)
kind = :content
if scan heredoc_delimiter
tokens << ["\n", :content]
tokens << [matched, :delimiter]
tokens << [:close, :string]
heredoc_delimiter = nil
label_expected = false
states.pop
next
end
elsif scan(/\\(?:x[0-9A-Fa-f]{1,2}|[0-7]{1,3})/)
kind = :char
elsif scan(heredoc_delimiter ? /\\[nrtvf\\$]/ : (delimiter == '"' ? /\\[nrtvf\\$"]/ : /\\[nrtvf\\$`]/))
kind = :char
elsif scan(/\\./m)
kind = :content
elsif scan(/\\/)
kind = :error
elsif match = scan(/#{RE::VARIABLE}/o)
kind = :local_variable
if check(/\[#{RE::IDENTIFIER}\]/o)
tokens << [:open, :inline]
tokens << [match, :local_variable]
tokens << [scan(/\[/), :operator]
tokens << [scan(/#{RE::IDENTIFIER}/o), :ident]
tokens << [scan(/\]/), :operator]
tokens << [:close, :inline]
next
elsif check(/\[/)
match << scan(/\[['"]?#{RE::IDENTIFIER}?['"]?\]?/o)
kind = :error
elsif check(/->#{RE::IDENTIFIER}/o)
tokens << [:open, :inline]
tokens << [match, :local_variable]
tokens << [scan(/->/), :operator]
tokens << [scan(/#{RE::IDENTIFIER}/o), :ident]
tokens << [:close, :inline]
next
elsif check(/->/)
match << scan(/->/)
kind = :error
end
elsif match = scan(/\{/)
if check(/\$/)
kind = :delimiter
states[-1] = [states.last, delimiter]
delimiter = nil
states.push :php
tokens << [:open, :inline]
else
kind = :string
end
elsif scan(/\$\{#{RE::IDENTIFIER}\}/o)
kind = :local_variable
elsif scan(/\$/)
kind = :content
end
when :class_expected
if scan(/\s+/)
kind = :space
elsif match = scan(/#{RE::IDENTIFIER}/o)
kind = :class
states.pop
else
states.pop
next
end
when :function_expected
if scan(/\s+/)
kind = :space
elsif scan(/&/)
kind = :operator
elsif match = scan(/#{RE::IDENTIFIER}/o)
kind = :function
states.pop
else
states.pop
next
end
else
raise_inspect 'Unknown state!', tokens, states
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, states
end
raise_inspect 'Empty token', tokens, states unless match
tokens << [match, kind]
end
tokens
end
end
end
end

View File

@ -1,21 +0,0 @@
module CodeRay
module Scanners
class Plaintext < Scanner
register_for :plaintext, :plain
title 'Plain text'
include Streamable
KINDS_NOT_LOC = [:plain]
def scan_tokens tokens, options
text = (scan_until(/\z/) || '')
tokens << [text, :plain]
end
end
end
end

View File

@ -1,285 +0,0 @@
module CodeRay
module Scanners
# Bases on pygments' PythonLexer, see
# http://dev.pocoo.org/projects/pygments/browser/pygments/lexers/agile.py.
class Python < Scanner
include Streamable
register_for :python
file_extension 'py'
KEYWORDS = [
'and', 'as', 'assert', 'break', 'class', 'continue', 'def',
'del', 'elif', 'else', 'except', 'finally', 'for',
'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not',
'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield',
'nonlocal', # new in Python 3
]
OLD_KEYWORDS = [
'exec', 'print', # gone in Python 3
]
PREDEFINED_METHODS_AND_TYPES = %w[
__import__ abs all any apply basestring bin bool buffer
bytearray bytes callable chr classmethod cmp coerce compile
complex delattr dict dir divmod enumerate eval execfile exit
file filter float frozenset getattr globals hasattr hash hex id
input int intern isinstance issubclass iter len list locals
long map max min next object oct open ord pow property range
raw_input reduce reload repr reversed round set setattr slice
sorted staticmethod str sum super tuple type unichr unicode
vars xrange zip
]
PREDEFINED_EXCEPTIONS = %w[
ArithmeticError AssertionError AttributeError
BaseException DeprecationWarning EOFError EnvironmentError
Exception FloatingPointError FutureWarning GeneratorExit IOError
ImportError ImportWarning IndentationError IndexError KeyError
KeyboardInterrupt LookupError MemoryError NameError
NotImplemented NotImplementedError OSError OverflowError
OverflowWarning PendingDeprecationWarning ReferenceError
RuntimeError RuntimeWarning StandardError StopIteration
SyntaxError SyntaxWarning SystemError SystemExit TabError
TypeError UnboundLocalError UnicodeDecodeError
UnicodeEncodeError UnicodeError UnicodeTranslateError
UnicodeWarning UserWarning ValueError Warning ZeroDivisionError
]
PREDEFINED_VARIABLES_AND_CONSTANTS = [
'False', 'True', 'None', # "keywords" since Python 3
'self', 'Ellipsis', 'NotImplemented',
]
IDENT_KIND = WordList.new(:ident).
add(KEYWORDS, :keyword).
add(OLD_KEYWORDS, :old_keyword).
add(PREDEFINED_METHODS_AND_TYPES, :predefined).
add(PREDEFINED_VARIABLES_AND_CONSTANTS, :pre_constant).
add(PREDEFINED_EXCEPTIONS, :exception)
NAME = / [^\W\d] \w* /x
ESCAPE = / [abfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} | N\{[-\w ]+\} /x
OPERATOR = /
\.\.\. | # ellipsis
\.(?!\d) | # dot but not decimal point
[,;:()\[\]{}] | # simple delimiters
\/\/=? | \*\*=? | # special math
[-+*\/%&|^]=? | # ordinary math and binary logic
[~`] | # binary complement and inspection
<<=? | >>=? | [<>=]=? | != # comparison and assignment
/x
STRING_DELIMITER_REGEXP = Hash.new do |h, delimiter|
h[delimiter] = Regexp.union delimiter
end
STRING_CONTENT_REGEXP = Hash.new do |h, delimiter|
h[delimiter] = / [^\\\n]+? (?= \\ | $ | #{Regexp.escape(delimiter)} ) /x
end
DEF_NEW_STATE = WordList.new(:initial).
add(%w(def), :def_expected).
add(%w(import from), :include_expected).
add(%w(class), :class_expected)
DESCRIPTOR = /
#{NAME}
(?: \. #{NAME} )*
| \*
/x
def scan_tokens tokens, options
state = :initial
string_delimiter = nil
string_raw = false
import_clause = class_name_follows = last_token_dot = false
unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8'
from_import_state = []
until eos?
kind = nil
match = nil
if state == :string
if scan(STRING_DELIMITER_REGEXP[string_delimiter])
tokens << [matched, :delimiter]
tokens << [:close, :string]
state = :initial
next
elsif string_delimiter.size == 3 && scan(/\n/)
kind = :content
elsif scan(STRING_CONTENT_REGEXP[string_delimiter])
kind = :content
elsif !string_raw && scan(/ \\ #{ESCAPE} /ox)
kind = :char
elsif scan(/ \\ #{UNICODE_ESCAPE} /ox)
kind = :char
elsif scan(/ \\ . /x)
kind = :content
elsif scan(/ \\ | $ /x)
tokens << [:close, :string]
kind = :error
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens, state
end
elsif match = scan(/ [ \t]+ | \\\n /x)
tokens << [match, :space]
next
elsif match = scan(/\n/)
tokens << [match, :space]
state = :initial if state == :include_expected
next
elsif match = scan(/ \# [^\n]* /mx)
tokens << [match, :comment]
next
elsif state == :initial
if scan(/#{OPERATOR}/o)
kind = :operator
elsif match = scan(/(u?r?|b)?("""|"|'''|')/i)
tokens << [:open, :string]
string_delimiter = self[2]
string_raw = false
modifiers = self[1]
unless modifiers.empty?
string_raw = !!modifiers.index(?r)
tokens << [modifiers, :modifier]
match = string_delimiter
end
state = :string
kind = :delimiter
# TODO: backticks
elsif match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o)
kind = IDENT_KIND[match]
# TODO: keyword arguments
kind = :ident if last_token_dot
if kind == :old_keyword
kind = check(/\(/) ? :ident : :keyword
elsif kind == :predefined && check(/ *=/)
kind = :ident
elsif kind == :keyword
state = DEF_NEW_STATE[match]
from_import_state << match.to_sym if state == :include_expected
end
elsif scan(/@[a-zA-Z0-9_.]+[lL]?/)
kind = :decorator
elsif scan(/0[xX][0-9A-Fa-f]+[lL]?/)
kind = :hex
elsif scan(/0[bB][01]+[lL]?/)
kind = :bin
elsif match = scan(/(?:\d*\.\d+|\d+\.\d*)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/)
kind = :float
if scan(/[jJ]/)
match << matched
kind = :imaginary
end
elsif scan(/0[oO][0-7]+|0[0-7]+(?![89.eE])[lL]?/)
kind = :oct
elsif match = scan(/\d+([lL])?/)
kind = :integer
if self[1] == nil && scan(/[jJ]/)
match << matched
kind = :imaginary
end
else
getch
kind = :error
end
elsif state == :def_expected
state = :initial
if match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o)
kind = :method
else
next
end
elsif state == :class_expected
state = :initial
if match = scan(unicode ? /#{NAME}/uo : /#{NAME}/o)
kind = :class
else
next
end
elsif state == :include_expected
if match = scan(unicode ? /#{DESCRIPTOR}/uo : /#{DESCRIPTOR}/o)
kind = :include
if match == 'as'
kind = :keyword
from_import_state << :as
elsif from_import_state.first == :from && match == 'import'
kind = :keyword
from_import_state << :import
elsif from_import_state.last == :as
# kind = match[0,1][unicode ? /[[:upper:]]/u : /[[:upper:]]/] ? :class : :method
kind = :ident
from_import_state.pop
elsif IDENT_KIND[match] == :keyword
unscan
match = nil
state = :initial
next
end
elsif match = scan(/,/)
from_import_state.pop if from_import_state.last == :as
kind = :operator
else
from_import_state = []
state = :initial
next
end
else
raise_inspect 'Unknown state', tokens, state
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
end
raise_inspect 'Empty token', tokens, state unless match
last_token_dot = match == '.'
tokens << [match, kind]
end
if state == :string
tokens << [:close, :string]
end
tokens
end
end
end
end

View File

@ -1,78 +0,0 @@
module CodeRay
module Scanners
load :html
load :ruby
# RHTML Scanner
class RHTML < Scanner
include Streamable
register_for :rhtml
title 'HTML ERB Template'
KINDS_NOT_LOC = HTML::KINDS_NOT_LOC
ERB_RUBY_BLOCK = /
<%(?!%)[=-]?
(?>
[^\-%]* # normal*
(?> # special
(?: %(?!>) | -(?!%>) )
[^\-%]* # normal*
)*
)
(?: -?%> )?
/x
START_OF_ERB = /
<%(?!%)
/x
private
def setup
@ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true
@html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true
end
def reset_instance
super
@html_scanner.reset
end
def scan_tokens tokens, options
until eos?
if (match = scan_until(/(?=#{START_OF_ERB})/o) || scan_until(/\z/)) and not match.empty?
@html_scanner.tokenize match
elsif match = scan(/#{ERB_RUBY_BLOCK}/o)
start_tag = match[/\A<%[-=#]?/]
end_tag = match[/-?%?>?\z/]
tokens << [:open, :inline]
tokens << [start_tag, :inline_delimiter]
code = match[start_tag.size .. -1 - end_tag.size]
if start_tag == '<%#'
tokens << [code, :comment]
else
@ruby_scanner.tokenize code
end
tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
tokens << [:close, :inline]
else
raise_inspect 'else-case reached!', tokens
end
end
tokens
end
end
end
end

View File

@ -1,444 +0,0 @@
# encoding: utf-8
module CodeRay
module Scanners
# This scanner is really complex, since Ruby _is_ a complex language!
#
# It tries to highlight 100% of all common code,
# and 90% of strange codes.
#
# It is optimized for HTML highlighting, and is not very useful for
# parsing or pretty printing.
#
# For now, I think it's better than the scanners in VIM or Syntax, or
# any highlighter I was able to find, except Caleb's RubyLexer.
#
# I hope it's also better than the rdoc/irb lexer.
class Ruby < Scanner
include Streamable
register_for :ruby
file_extension 'rb'
helper :patterns
if not defined? EncodingError
EncodingError = Class.new Exception
end
private
def scan_tokens tokens, options
if string.respond_to?(:encoding)
unless string.encoding == Encoding::UTF_8
self.string = string.encode Encoding::UTF_8,
:invalid => :replace, :undef => :replace, :replace => '?'
end
unicode = false
else
unicode = exist?(/[^\x00-\x7f]/)
end
last_token_dot = false
value_expected = true
heredocs = nil
last_state = nil
state = :initial
depth = nil
inline_block_stack = []
patterns = Patterns # avoid constant lookup
until eos?
match = nil
kind = nil
if state.instance_of? patterns::StringState
# {{{
match = scan_until(state.pattern) || scan_until(/\z/)
tokens << [match, :content] unless match.empty?
break if eos?
if state.heredoc and self[1] # end of heredoc
match = getch.to_s
match << scan_until(/$/) unless eos?
tokens << [match, :delimiter]
tokens << [:close, state.type]
state = state.next_state
next
end
case match = getch
when state.delim
if state.paren
state.paren_depth -= 1
if state.paren_depth > 0
tokens << [match, :nesting_delimiter]
next
end
end
tokens << [match, :delimiter]
if state.type == :regexp and not eos?
modifiers = scan(/#{patterns::REGEXP_MODIFIERS}/ox)
tokens << [modifiers, :modifier] unless modifiers.empty?
end
tokens << [:close, state.type]
value_expected = false
state = state.next_state
when '\\'
if state.interpreted
if esc = scan(/ #{patterns::ESCAPE} /ox)
tokens << [match + esc, :char]
else
tokens << [match, :error]
end
else
case m = getch
when state.delim, '\\'
tokens << [match + m, :char]
when nil
tokens << [match, :error]
else
tokens << [match + m, :content]
end
end
when '#'
case peek(1)
when '{'
inline_block_stack << [state, depth, heredocs]
value_expected = true
state = :initial
depth = 1
tokens << [:open, :inline]
tokens << [match + getch, :inline_delimiter]
when '$', '@'
tokens << [match, :escape]
last_state = state # scan one token as normal code, then return here
state = :initial
else
raise_inspect 'else-case # reached; #%p not handled' % peek(1), tokens
end
when state.paren
state.paren_depth += 1
tokens << [match, :nesting_delimiter]
when /#{patterns::REGEXP_SYMBOLS}/ox
tokens << [match, :function]
else
raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], tokens
end
next
# }}}
else
# {{{
if match = scan(/[ \t\f]+/)
kind = :space
match << scan(/\s*/) unless eos? || heredocs
value_expected = true if match.index(?\n)
tokens << [match, kind]
next
elsif match = scan(/\\?\n/)
kind = :space
if match == "\n"
value_expected = true
state = :initial if state == :undef_comma_expected
end
if heredocs
unscan # heredoc scanning needs \n at start
state = heredocs.shift
tokens << [:open, state.type]
heredocs = nil if heredocs.empty?
next
else
match << scan(/\s*/) unless eos?
end
tokens << [match, kind]
next
elsif bol? && match = scan(/\#!.*/)
tokens << [match, :doctype]
next
elsif match = scan(/\#.*/) or
( bol? and match = scan(/#{patterns::RUBYDOC_OR_DATA}/o) )
kind = :comment
tokens << [match, kind]
next
elsif state == :initial
# IDENTS #
if match = scan(unicode ? /#{patterns::METHOD_NAME}/uo :
/#{patterns::METHOD_NAME}/o)
if last_token_dot
kind = if match[/^[A-Z]/] and not match?(/\(/) then :constant else :ident end
else
if value_expected != :expect_colon && scan(/:(?= )/)
tokens << [match, :key]
match = ':'
kind = :operator
else
kind = patterns::IDENT_KIND[match]
if kind == :ident
if match[/\A[A-Z]/] and not match[/[!?]$/] and not match?(/\(/)
kind = :constant
end
elsif kind == :reserved
state = patterns::DEF_NEW_STATE[match]
value_expected = :set if patterns::KEYWORDS_EXPECTING_VALUE[match]
end
end
end
value_expected = :set if check(/#{patterns::VALUE_FOLLOWS}/o)
elsif last_token_dot and match = scan(/#{patterns::METHOD_NAME_OPERATOR}|\(/o)
kind = :ident
value_expected = :set if check(unicode ? /#{patterns::VALUE_FOLLOWS}/uo :
/#{patterns::VALUE_FOLLOWS}/o)
# OPERATORS #
elsif not last_token_dot and match = scan(/ \.\.\.? | (?:\.|::)() | [,\(\)\[\]\{\}] | ==?=? /x)
if match !~ / [.\)\]\}] /x or match =~ /\.\.\.?/
value_expected = :set
end
last_token_dot = :set if self[1]
kind = :operator
unless inline_block_stack.empty?
case match
when '{'
depth += 1
when '}'
depth -= 1
if depth == 0 # closing brace of inline block reached
state, depth, heredocs = inline_block_stack.pop
heredocs = nil if heredocs && heredocs.empty?
tokens << [match, :inline_delimiter]
kind = :inline
match = :close
end
end
end
elsif match = scan(/ ['"] /mx)
tokens << [:open, :string]
kind = :delimiter
state = patterns::StringState.new :string, match == '"', match # important for streaming
elsif match = scan(unicode ? /#{patterns::INSTANCE_VARIABLE}/uo :
/#{patterns::INSTANCE_VARIABLE}/o)
kind = :instance_variable
elsif value_expected and match = scan(/\//)
tokens << [:open, :regexp]
kind = :delimiter
interpreted = true
state = patterns::StringState.new :regexp, interpreted, match
# elsif match = scan(/[-+]?#{patterns::NUMERIC}/o)
elsif match = value_expected ? scan(/[-+]?#{patterns::NUMERIC}/o) : scan(/#{patterns::NUMERIC}/o)
kind = self[1] ? :float : :integer
elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo :
/#{patterns::SYMBOL}/o)
case delim = match[1]
when ?', ?"
tokens << [:open, :symbol]
tokens << [':', :symbol]
match = delim.chr
kind = :delimiter
state = patterns::StringState.new :symbol, delim == ?", match
else
kind = :symbol
end
elsif match = scan(/ -[>=]? | [+!~^]=? | [*|&]{1,2}=? | >>? /x)
value_expected = :set
kind = :operator
elsif value_expected and match = scan(unicode ? /#{patterns::HEREDOC_OPEN}/uo :
/#{patterns::HEREDOC_OPEN}/o)
indented = self[1] == '-'
quote = self[3]
delim = self[quote ? 4 : 2]
kind = patterns::QUOTE_TO_TYPE[quote]
tokens << [:open, kind]
tokens << [match, :delimiter]
match = :close
heredoc = patterns::StringState.new kind, quote != '\'', delim, (indented ? :indented : :linestart )
heredocs ||= [] # create heredocs if empty
heredocs << heredoc
elsif value_expected and match = scan(/#{patterns::FANCY_START_CORRECT}/o)
kind, interpreted = *patterns::FancyStringType.fetch(self[1]) do
raise_inspect 'Unknown fancy string: %%%p' % k, tokens
end
tokens << [:open, kind]
state = patterns::StringState.new kind, interpreted, self[2]
kind = :delimiter
elsif value_expected and match = scan(unicode ? /#{patterns::CHARACTER}/uo :
/#{patterns::CHARACTER}/o)
kind = :integer
elsif match = scan(/ [\/%]=? | <(?:<|=>?)? | [?:;] /x)
value_expected = :set
kind = :operator
elsif match = scan(/`/)
if last_token_dot
kind = :operator
else
tokens << [:open, :shell]
kind = :delimiter
state = patterns::StringState.new :shell, true, match
end
elsif match = scan(unicode ? /#{patterns::GLOBAL_VARIABLE}/uo :
/#{patterns::GLOBAL_VARIABLE}/o)
kind = :global_variable
elsif match = scan(unicode ? /#{patterns::CLASS_VARIABLE}/uo :
/#{patterns::CLASS_VARIABLE}/o)
kind = :class_variable
else
if !unicode && !string.respond_to?(:encoding)
# check for unicode
debug, $DEBUG = $DEBUG, false
begin
if check(/./mu).size > 1
# seems like we should try again with unicode
unicode = true
end
rescue
# bad unicode char; use getch
ensure
$DEBUG = debug
end
next if unicode
end
kind = :error
match = scan(unicode ? /./mu : /./m)
end
elsif state == :def_expected
state = :initial
if scan(/self\./)
tokens << ['self', :pre_constant]
tokens << ['.', :operator]
end
if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
/(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
kind = :method
else
next
end
elsif state == :module_expected
if match = scan(/<</)
kind = :operator
else
state = :initial
if match = scan(unicode ? /(?:#{patterns::IDENT}::)*#{patterns::IDENT}/uo :
/(?:#{patterns::IDENT}::)*#{patterns::IDENT}/o)
kind = :class
else
next
end
end
elsif state == :undef_expected
state = :undef_comma_expected
if match = scan(unicode ? /#{patterns::METHOD_NAME_EX}/uo :
/#{patterns::METHOD_NAME_EX}/o)
kind = :method
elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo :
/#{patterns::SYMBOL}/o)
case delim = match[1]
when ?', ?"
tokens << [:open, :symbol]
tokens << [':', :symbol]
match = delim.chr
kind = :delimiter
state = patterns::StringState.new :symbol, delim == ?", match
state.next_state = :undef_comma_expected
else
kind = :symbol
end
else
state = :initial
next
end
elsif state == :alias_expected
match = scan(unicode ? /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/uo :
/(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o)
if match
tokens << [self[1], (self[1][0] == ?: ? :symbol : :method)]
tokens << [self[2], :space]
tokens << [self[3], (self[3][0] == ?: ? :symbol : :method)]
end
state = :initial
next
elsif state == :undef_comma_expected
if match = scan(/,/)
kind = :operator
state = :undef_expected
else
state = :initial
next
end
end
# }}}
unless kind == :error
if value_expected = value_expected == :set
value_expected = :expect_colon if match == '?' || match == 'when'
end
last_token_dot = last_token_dot == :set
end
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
if last_state
state = last_state
last_state = nil
end
end
end
inline_block_stack << [state] if state.is_a? patterns::StringState
until inline_block_stack.empty?
this_block = inline_block_stack.pop
tokens << [:close, :inline] if this_block.size > 1
state = this_block.first
tokens << [:close, state.type]
end
tokens
end
end
end
end
# vim:fdm=marker

View File

@ -1,252 +0,0 @@
# encoding: utf-8
module CodeRay
module Scanners
module Ruby::Patterns # :nodoc:
RESERVED_WORDS = %w[
and def end in or unless begin
defined? ensure module redo super until
BEGIN break do next rescue then
when END case else for retry
while alias class elsif if not return
undef yield
]
DEF_KEYWORDS = %w[ def ]
UNDEF_KEYWORDS = %w[ undef ]
ALIAS_KEYWORDS = %w[ alias ]
MODULE_KEYWORDS = %w[ class module ]
DEF_NEW_STATE = WordList.new(:initial).
add(DEF_KEYWORDS, :def_expected).
add(UNDEF_KEYWORDS, :undef_expected).
add(ALIAS_KEYWORDS, :alias_expected).
add(MODULE_KEYWORDS, :module_expected)
PREDEFINED_CONSTANTS = %w[
nil true false self
DATA ARGV ARGF
__FILE__ __LINE__ __ENCODING__
]
IDENT_KIND = WordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_CONSTANTS, :pre_constant)
if /\w/u === '∑'
# MRI 1.8.6, 1.8.7
IDENT = /[^\W\d]\w*/
else
if //.respond_to? :encoding
# MRI 1.9.1, 1.9.2
IDENT = Regexp.new '[\p{L}\p{M}\p{Pc}\p{Sm}&&[^\x00-\x40\x5b-\x5e\x60\x7b-\x7f]][\p{L}\p{M}\p{N}\p{Pc}\p{Sm}&&[^\x00-\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\x7f]]*'
else
# JRuby, Rubinius
IDENT = /[^\x00-\x40\x5b-\x5e\x60\x7b-\x7f][^\x00-\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\x7f]*/
end
end
METHOD_NAME = / #{IDENT} [?!]? /ox
METHOD_NAME_OPERATOR = /
\*\*? # multiplication and power
| [-+~]@? # plus, minus, tilde with and without at sign
| [\/%&|^`] # division, modulo or format strings, and, or, xor, system
| \[\]=? # array getter and setter
| << | >> # append or shift left, shift right
| <=?>? | >=? # comparison, rocket operator
| ===? | =~ # simple equality, case equality, match
| ![~=@]? # negation with and without at sign, not-equal and not-match
/ox
METHOD_NAME_EX = / #{IDENT} (?:[?!]|=(?!>))? | #{METHOD_NAME_OPERATOR} /ox
INSTANCE_VARIABLE = / @ #{IDENT} /ox
CLASS_VARIABLE = / @@ #{IDENT} /ox
OBJECT_VARIABLE = / @@? #{IDENT} /ox
GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox
PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} | #{OBJECT_VARIABLE} /ox
VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox
QUOTE_TO_TYPE = {
'`' => :shell,
'/'=> :regexp,
}
QUOTE_TO_TYPE.default = :string
REGEXP_MODIFIERS = /[mixounse]*/
REGEXP_SYMBOLS = /[|?*+(){}\[\].^$]/
DECIMAL = /\d+(?:_\d+)*/
OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
BINARY = /0b[01]+(?:_[01]+)*/
EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
FLOAT_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox
FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox
NUMERIC = / (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox
SYMBOL = /
:
(?:
#{METHOD_NAME_EX}
| #{PREFIX_VARIABLE}
| ['"]
)
/ox
METHOD_NAME_OR_SYMBOL = / #{METHOD_NAME_EX} | #{SYMBOL} /ox
SIMPLE_ESCAPE = /
[abefnrstv]
| [0-7]{1,3}
| x[0-9A-Fa-f]{1,2}
| .?
/mx
CONTROL_META_ESCAPE = /
(?: M-|C-|c )
(?: \\ (?: M-|C-|c ) )*
(?: [^\\] | \\ #{SIMPLE_ESCAPE} )?
/mox
ESCAPE = /
#{CONTROL_META_ESCAPE} | #{SIMPLE_ESCAPE}
/mox
CHARACTER = /
\?
(?:
[^\s\\]
| \\ #{ESCAPE}
)
/mox
# NOTE: This is not completely correct, but
# nobody needs heredoc delimiters ending with \n.
# Also, delimiters starting with numbers are allowed.
# but they are more often than not a false positive.
HEREDOC_OPEN = /
<< (-)? # $1 = float
(?:
( #{IDENT} ) # $2 = delim
|
( ["'`\/] ) # $3 = quote, type
( [^\n]*? ) \3 # $4 = delim
)
/mx
RUBYDOC = /
=begin (?!\S)
.*?
(?: \Z | ^=end (?!\S) [^\n]* )
/mx
DATA = /
__END__$
.*?
(?: \Z | (?=^\#CODE) )
/mx
# Checks for a valid value to follow. This enables
# value_expected in method calls without parentheses.
VALUE_FOLLOWS = /
(?>[ \t\f\v]+)
(?:
[%\/][^\s=]
| <<-?\S
| [-+] \d
| #{CHARACTER}
)
/x
KEYWORDS_EXPECTING_VALUE = WordList.new.add(%w[
and end in or unless begin
defined? ensure redo super until
break do next rescue then
when case else for retry
while elsif if not return
yield
])
RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo
RDOC_DATA_START = / ^=begin (?!\S) | ^__END__$ /x
FANCY_START_CORRECT = / % ( [qQwWxsr] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /mx
FancyStringType = {
'q' => [:string, false],
'Q' => [:string, true],
'r' => [:regexp, true],
's' => [:symbol, false],
'x' => [:shell, true]
}
FancyStringType['w'] = FancyStringType['q']
FancyStringType['W'] = FancyStringType[''] = FancyStringType['Q']
class StringState < Struct.new :type, :interpreted, :delim, :heredoc,
:paren, :paren_depth, :pattern, :next_state
CLOSING_PAREN = Hash[ *%w[
( )
[ ]
< >
{ }
] ]
CLOSING_PAREN.each { |k,v| k.freeze; v.freeze } # debug, if I try to change it with <<
OPENING_PAREN = CLOSING_PAREN.invert
STRING_PATTERN = Hash.new do |h, k|
delim, interpreted = *k
delim_pattern = Regexp.escape(delim.dup) # dup: workaround for old Ruby
if closing_paren = CLOSING_PAREN[delim]
delim_pattern = delim_pattern[0..-1] if defined? JRUBY_VERSION # JRuby fix
delim_pattern << Regexp.escape(closing_paren)
end
delim_pattern << '\\\\' unless delim == '\\'
special_escapes =
case interpreted
when :regexp_symbols
'| ' + REGEXP_SYMBOLS.source
when :words
'| \s'
end
h[k] =
if interpreted and not delim == '#'
/ (?= [#{delim_pattern}] | \# [{$@] #{special_escapes} ) /mx
else
/ (?= [#{delim_pattern}] #{special_escapes} ) /mx
end
end
HEREDOC_PATTERN = Hash.new do |h, k|
delim, interpreted, indented = *k
delim_pattern = Regexp.escape(delim.dup) # dup: workaround for old Ruby
delim_pattern = / \n #{ '(?>[\ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x
h[k] =
if interpreted
/ (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc
else
/ (?= #{delim_pattern}() | \\ ) /mx
end
end
def initialize kind, interpreted, delim, heredoc = false
if heredoc
pattern = HEREDOC_PATTERN[ [delim, interpreted, heredoc == :indented] ]
delim = nil
else
pattern = STRING_PATTERN[ [delim, interpreted] ]
if paren = CLOSING_PAREN[delim]
delim, paren = paren, delim
paren_depth = 1
end
end
super kind, interpreted, delim, heredoc, paren, paren_depth, pattern, :initial
end
end unless defined? StringState
end
end
end

View File

@ -1,145 +0,0 @@
module CodeRay
module Scanners
# Scheme scanner for CodeRay (by closure).
# Thanks to murphy for putting CodeRay into public.
class Scheme < Scanner
# TODO: function defs
# TODO: built-in functions
register_for :scheme
file_extension 'scm'
CORE_FORMS = %w[
lambda let let* letrec syntax-case define-syntax let-syntax
letrec-syntax begin define quote if or and cond case do delay
quasiquote set! cons force call-with-current-continuation call/cc
]
IDENT_KIND = CaseIgnoringWordList.new(:ident).
add(CORE_FORMS, :reserved)
#IDENTIFIER_INITIAL = /[a-z!@\$%&\*\/\:<=>\?~_\^]/i
#IDENTIFIER_SUBSEQUENT = /#{IDENTIFIER_INITIAL}|\d|\.|\+|-/
#IDENTIFIER = /#{IDENTIFIER_INITIAL}#{IDENTIFIER_SUBSEQUENT}*|\+|-|\.{3}/
IDENTIFIER = /[a-zA-Z!@$%&*\/:<=>?~_^][\w!@$%&*\/:<=>?~^.+\-]*|[+-]|\.\.\./
DIGIT = /\d/
DIGIT10 = DIGIT
DIGIT16 = /[0-9a-f]/i
DIGIT8 = /[0-7]/
DIGIT2 = /[01]/
RADIX16 = /\#x/i
RADIX8 = /\#o/i
RADIX2 = /\#b/i
RADIX10 = /\#d/i
EXACTNESS = /#i|#e/i
SIGN = /[\+-]?/
EXP_MARK = /[esfdl]/i
EXP = /#{EXP_MARK}#{SIGN}#{DIGIT}+/
SUFFIX = /#{EXP}?/
PREFIX10 = /#{RADIX10}?#{EXACTNESS}?|#{EXACTNESS}?#{RADIX10}?/
PREFIX16 = /#{RADIX16}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX16}/
PREFIX8 = /#{RADIX8}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX8}/
PREFIX2 = /#{RADIX2}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX2}/
UINT10 = /#{DIGIT10}+#*/
UINT16 = /#{DIGIT16}+#*/
UINT8 = /#{DIGIT8}+#*/
UINT2 = /#{DIGIT2}+#*/
DECIMAL = /#{DIGIT10}+#+\.#*#{SUFFIX}|#{DIGIT10}+\.#{DIGIT10}*#*#{SUFFIX}|\.#{DIGIT10}+#*#{SUFFIX}|#{UINT10}#{EXP}/
UREAL10 = /#{UINT10}\/#{UINT10}|#{DECIMAL}|#{UINT10}/
UREAL16 = /#{UINT16}\/#{UINT16}|#{UINT16}/
UREAL8 = /#{UINT8}\/#{UINT8}|#{UINT8}/
UREAL2 = /#{UINT2}\/#{UINT2}|#{UINT2}/
REAL10 = /#{SIGN}#{UREAL10}/
REAL16 = /#{SIGN}#{UREAL16}/
REAL8 = /#{SIGN}#{UREAL8}/
REAL2 = /#{SIGN}#{UREAL2}/
IMAG10 = /i|#{UREAL10}i/
IMAG16 = /i|#{UREAL16}i/
IMAG8 = /i|#{UREAL8}i/
IMAG2 = /i|#{UREAL2}i/
COMPLEX10 = /#{REAL10}@#{REAL10}|#{REAL10}\+#{IMAG10}|#{REAL10}-#{IMAG10}|\+#{IMAG10}|-#{IMAG10}|#{REAL10}/
COMPLEX16 = /#{REAL16}@#{REAL16}|#{REAL16}\+#{IMAG16}|#{REAL16}-#{IMAG16}|\+#{IMAG16}|-#{IMAG16}|#{REAL16}/
COMPLEX8 = /#{REAL8}@#{REAL8}|#{REAL8}\+#{IMAG8}|#{REAL8}-#{IMAG8}|\+#{IMAG8}|-#{IMAG8}|#{REAL8}/
COMPLEX2 = /#{REAL2}@#{REAL2}|#{REAL2}\+#{IMAG2}|#{REAL2}-#{IMAG2}|\+#{IMAG2}|-#{IMAG2}|#{REAL2}/
NUM10 = /#{PREFIX10}?#{COMPLEX10}/
NUM16 = /#{PREFIX16}#{COMPLEX16}/
NUM8 = /#{PREFIX8}#{COMPLEX8}/
NUM2 = /#{PREFIX2}#{COMPLEX2}/
NUM = /#{NUM10}|#{NUM16}|#{NUM8}|#{NUM2}/
private
def scan_tokens tokens,options
state = :initial
ident_kind = IDENT_KIND
until eos?
kind = match = nil
case state
when :initial
if scan(/ \s+ | \\\n /x)
kind = :space
elsif scan(/['\(\[\)\]]|#\(/)
kind = :operator_fat
elsif scan(/;.*/)
kind = :comment
elsif scan(/#\\(?:newline|space|.?)/)
kind = :char
elsif scan(/#[ft]/)
kind = :pre_constant
elsif scan(/#{IDENTIFIER}/o)
kind = ident_kind[matched]
elsif scan(/\./)
kind = :operator
elsif scan(/"/)
tokens << [:open, :string]
state = :string
tokens << ['"', :delimiter]
next
elsif scan(/#{NUM}/o) and not matched.empty?
kind = :integer
elsif getch
kind = :error
end
when :string
if scan(/[^"\\]+/) or scan(/\\.?/)
kind = :content
elsif scan(/"/)
tokens << ['"', :delimiter]
tokens << [:close, :string]
state = :initial
next
else
raise_inspect "else case \" reached; %p not handled." % peek(1),
tokens, state
end
else
raise "else case reached"
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens, state unless match
tokens << [match, kind]
end # until eos
if state == :string
tokens << [:close, :string]
end
tokens
end #scan_tokens
end #class
end #module scanners
end #module coderay

View File

@ -1,162 +0,0 @@
module CodeRay module Scanners
# by Josh Goebel
class SQL < Scanner
register_for :sql
RESERVED_WORDS = %w(
create database table index trigger drop primary key set select
insert update delete replace into
on from values before and or if exists case when
then else as group order by avg where
join inner outer union engine not
like end using collate show columns begin
)
PREDEFINED_TYPES = %w(
char varchar enum binary text tinytext mediumtext
longtext blob tinyblob mediumblob longblob timestamp
date time datetime year double decimal float int
integer tinyint mediumint bigint smallint unsigned bit
bool boolean hex bin oct
)
PREDEFINED_FUNCTIONS = %w( sum cast abs pi count min max avg )
DIRECTIVES = %w( auto_increment unique default charset )
PREDEFINED_CONSTANTS = %w( null true false )
IDENT_KIND = CaseIgnoringWordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_TYPES, :pre_type).
add(PREDEFINED_CONSTANTS, :pre_constant).
add(PREDEFINED_FUNCTIONS, :predefined).
add(DIRECTIVES, :directive)
ESCAPE = / [rbfntv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | . /mx
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
STRING_PREFIXES = /[xnb]|_\w+/i
def scan_tokens tokens, options
state = :initial
string_type = nil
string_content = ''
until eos?
kind = nil
match = nil
if state == :initial
if scan(/ \s+ | \\\n /x)
kind = :space
elsif scan(/(?:--\s?|#).*/)
kind = :comment
elsif scan(%r! /\* (?: .*? \*/ | .* ) !mx)
kind = :comment
elsif scan(/ [-+*\/=<>;,!&^|()\[\]{}~%] | \.(?!\d) /x)
kind = :operator
elsif scan(/(#{STRING_PREFIXES})?([`"'])/o)
prefix = self[1]
string_type = self[2]
tokens << [:open, :string]
tokens << [prefix, :modifier] if prefix
match = string_type
state = :string
kind = :delimiter
elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9]* /x)
kind = match[0] == ?@ ? :variable : IDENT_KIND[match.downcase]
elsif scan(/0[xX][0-9A-Fa-f]+/)
kind = :hex
elsif scan(/0[0-7]+(?![89.eEfF])/)
kind = :oct
elsif scan(/(?>\d+)(?![.eEfF])/)
kind = :integer
elsif scan(/\d[fF]|\d*\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/)
kind = :float
else
getch
kind = :error
end
elsif state == :string
if match = scan(/[^\\"'`]+/)
string_content << match
next
elsif match = scan(/["'`]/)
if string_type == match
if peek(1) == string_type # doubling means escape
string_content << string_type << getch
next
end
unless string_content.empty?
tokens << [string_content, :content]
string_content = ''
end
tokens << [matched, :delimiter]
tokens << [:close, :string]
state = :initial
string_type = nil
next
else
string_content << match
end
next
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
unless string_content.empty?
tokens << [string_content, :content]
string_content = ''
end
kind = :char
elsif match = scan(/ \\ . /mox)
string_content << match
next
elsif scan(/ \\ | $ /x)
unless string_content.empty?
tokens << [string_content, :content]
string_content = ''
end
kind = :error
state = :initial
else
raise "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise 'else-case reached', tokens
end
match ||= matched
unless kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
tokens
end
end
end end

View File

@ -1,17 +0,0 @@
module CodeRay
module Scanners
load :html
# XML Scanner
#
# Currently this is the same scanner as Scanners::HTML.
class XML < HTML
register_for :xml
file_extension 'xml'
end
end
end

View File

@ -1,140 +0,0 @@
module CodeRay
module Scanners
# YAML Scanner
#
# Based on the YAML scanner from Syntax by Jamis Buck.
class YAML < Scanner
register_for :yaml
file_extension 'yml'
KINDS_NOT_LOC = :all
def scan_tokens tokens, options
value_expected = nil
state = :initial
key_indent = indent = 0
until eos?
kind = nil
match = nil
key_indent = nil if bol?
if match = scan(/ +[\t ]*/)
kind = :space
elsif match = scan(/\n+/)
kind = :space
state = :initial if match.index(?\n)
elsif match = scan(/#.*/)
kind = :comment
elsif bol? and case
when match = scan(/---|\.\.\./)
tokens << [:open, :head]
tokens << [match, :head]
tokens << [:close, :head]
next
when match = scan(/%.*/)
tokens << [match, :doctype]
next
end
elsif state == :value and case
when !check(/(?:"[^"]*")(?=: |:$)/) && scan(/"/)
tokens << [:open, :string]
tokens << [matched, :delimiter]
tokens << [matched, :content] if scan(/ [^"\\]* (?: \\. [^"\\]* )* /mx)
tokens << [matched, :delimiter] if scan(/"/)
tokens << [:close, :string]
next
when match = scan(/[|>][-+]?/)
tokens << [:open, :string]
tokens << [match, :delimiter]
string_indent = key_indent || column(pos - match.size - 1)
tokens << [matched, :content] if scan(/(?:\n+ {#{string_indent + 1}}.*)+/)
tokens << [:close, :string]
next
when match = scan(/(?![!"*&]).+?(?=$|\s+#)/)
tokens << [match, :string]
string_indent = key_indent || column(pos - match.size - 1)
tokens << [matched, :string] if scan(/(?:\n+ {#{string_indent + 1}}.*)+/)
next
end
elsif case
when match = scan(/[-:](?= |$)/)
state = :value if state == :colon && (match == ':' || match == '-')
state = :value if state == :initial && match == '-'
kind = :operator
when match = scan(/[,{}\[\]]/)
kind = :operator
when state == :initial && match = scan(/[\w.() ]*\S(?=: |:$)/)
kind = :key
key_indent = column(pos - match.size - 1)
# tokens << [key_indent.inspect, :debug]
state = :colon
when match = scan(/(?:"[^"\n]*"|'[^'\n]*')(?=: |:$)/)
tokens << [:open, :key]
tokens << [match[0,1], :delimiter]
tokens << [match[1..-2], :content]
tokens << [match[-1,1], :delimiter]
tokens << [:close, :key]
key_indent = column(pos - match.size - 1)
# tokens << [key_indent.inspect, :debug]
state = :colon
next
when scan(/(![\w\/]+)(:([\w:]+))?/)
tokens << [self[1], :type]
if self[2]
tokens << [':', :operator]
tokens << [self[3], :class]
end
next
when scan(/&\S+/)
kind = :variable
when scan(/\*\w+/)
kind = :global_variable
when scan(/<</)
kind = :class_variable
when scan(/\d\d:\d\d:\d\d/)
kind = :oct
when scan(/\d\d\d\d-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d+)? [-+]\d\d:\d\d/)
kind = :oct
when scan(/:\w+/)
kind = :symbol
when scan(/[^:\s]+(:(?! |$)[^:\s]*)* .*/)
kind = :error
when scan(/[^:\s]+(:(?! |$)[^:\s]*)*/)
kind = :error
end
else
getch
kind = :error
end
match ||= matched
if $CODERAY_DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
end
raise_inspect 'Empty token', tokens, state unless match
tokens << [match, kind]
end
tokens
end
end
end
end

View File

@ -1,152 +0,0 @@
module CodeRay
module Styles
class Cycnus < Style
register_for :cycnus
code_background = '#f8f8f8'
numbers_background = '#def'
border_color = 'silver'
normal_color = '#000'
CSS_MAIN_STYLES = <<-MAIN
.CodeRay {
background-color: #{code_background};
border: 1px solid #{border_color};
font-family: 'Courier New', 'Terminal', monospace;
color: #{normal_color};
}
.CodeRay pre { margin: 0px }
div.CodeRay { }
span.CodeRay { white-space: pre; border: 0px; padding: 2px }
table.CodeRay { border-collapse: collapse; width: 100%; padding: 2px }
table.CodeRay td { padding: 2px 4px; vertical-align: top }
.CodeRay .line_numbers, .CodeRay .no {
background-color: #{numbers_background};
color: gray;
text-align: right;
}
.CodeRay .line_numbers tt { font-weight: bold }
.CodeRay .line_numbers .highlighted { color: red }
.CodeRay .line { display: block; float: left; width: 100%; }
.CodeRay .no { padding: 0px 4px }
.CodeRay .code { width: 100% }
ol.CodeRay { font-size: 10pt }
ol.CodeRay li { white-space: pre }
.CodeRay .code pre { overflow: auto }
MAIN
TOKEN_COLORS = <<-'TOKENS'
.debug { color:white ! important; background:blue ! important; }
.af { color:#00C }
.an { color:#007 }
.at { color:#f08 }
.av { color:#700 }
.aw { color:#C00 }
.bi { color:#509; font-weight:bold }
.c { color:#888; }
.ch { color:#04D }
.ch .k { color:#04D }
.ch .dl { color:#039 }
.cl { color:#B06; font-weight:bold }
.cm { color:#A08; font-weight:bold }
.co { color:#036; font-weight:bold }
.cr { color:#0A0 }
.cv { color:#369 }
.de { color:#B0B; }
.df { color:#099; font-weight:bold }
.di { color:#088; font-weight:bold }
.dl { color:black }
.do { color:#970 }
.dt { color:#34b }
.ds { color:#D42; font-weight:bold }
.e { color:#666; font-weight:bold }
.en { color:#800; font-weight:bold }
.er { color:#F00; background-color:#FAA }
.ex { color:#C00; font-weight:bold }
.fl { color:#60E; font-weight:bold }
.fu { color:#06B; font-weight:bold }
.gv { color:#d70; font-weight:bold }
.hx { color:#058; font-weight:bold }
.i { color:#00D; font-weight:bold }
.ic { color:#B44; font-weight:bold }
.il { background: #ddd; color: black }
.il .il { background: #ccc }
.il .il .il { background: #bbb }
.il .idl { background: #ddd; font-weight: bold; color: #666 }
.idl { background-color: #bbb; font-weight: bold; color: #666; }
.im { color:#f00; }
.in { color:#B2B; font-weight:bold }
.iv { color:#33B }
.la { color:#970; font-weight:bold }
.lv { color:#963 }
.oc { color:#40E; font-weight:bold }
.of { color:#000; font-weight:bold }
.op { }
.pc { color:#038; font-weight:bold }
.pd { color:#369; font-weight:bold }
.pp { color:#579; }
.ps { color:#00C; font-weight:bold }
.pt { color:#074; font-weight:bold }
.r, .kw { color:#080; font-weight:bold }
.ke { color: #808; }
.ke .dl { color: #606; }
.ke .ch { color: #80f; }
.vl { color: #088; }
.rx { background-color:#fff0ff }
.rx .k { color:#808 }
.rx .dl { color:#404 }
.rx .mod { color:#C2C }
.rx .fu { color:#404; font-weight: bold }
.s { background-color:#fff0f0; color: #D20; }
.s .s { background-color:#ffe0e0 }
.s .s .s { background-color:#ffd0d0 }
.s .k { }
.s .ch { color: #b0b; }
.s .dl { color: #710; }
.sh { background-color:#f0fff0; color:#2B2 }
.sh .k { }
.sh .dl { color:#161 }
.sy { color:#A60 }
.sy .k { color:#A60 }
.sy .dl { color:#630 }
.ta { color:#070 }
.tf { color:#070; font-weight:bold }
.ts { color:#D70; font-weight:bold }
.ty { color:#339; font-weight:bold }
.v { color:#036 }
.xt { color:#444 }
.ins { background: #afa; }
.del { background: #faa; }
.chg { color: #aaf; background: #007; }
.head { color: #f8f; background: #505 }
.ins .ins { color: #080; font-weight:bold }
.del .del { color: #800; font-weight:bold }
.chg .chg { color: #66f; }
.head .head { color: #f4f; }
TOKENS
end
end
end

View File

@ -1,134 +0,0 @@
module CodeRay
module Styles
class Murphy < Style
register_for :murphy
code_background = '#001129'
numbers_background = code_background
border_color = 'silver'
normal_color = '#C0C0C0'
CSS_MAIN_STYLES = <<-MAIN
.CodeRay {
background-color: #{code_background};
border: 1px solid #{border_color};
font-family: 'Courier New', 'Terminal', monospace;
color: #{normal_color};
}
.CodeRay pre { margin: 0px; }
div.CodeRay { }
span.CodeRay { white-space: pre; border: 0px; padding: 2px; }
table.CodeRay { border-collapse: collapse; width: 100%; padding: 2px; }
table.CodeRay td { padding: 2px 4px; vertical-align: top; }
.CodeRay .line_numbers, .CodeRay .no {
background-color: #{numbers_background};
color: gray;
text-align: right;
}
.CodeRay .line_numbers tt { font-weight: bold; }
.CodeRay .line_numbers .highlighted { color: red }
.CodeRay .line { display: block; float: left; width: 100%; }
.CodeRay .no { padding: 0px 4px; }
.CodeRay .code { width: 100%; }
ol.CodeRay { font-size: 10pt; }
ol.CodeRay li { white-space: pre; }
.CodeRay .code pre { overflow: auto; }
MAIN
TOKEN_COLORS = <<-'TOKENS'
.af { color:#00C; }
.an { color:#007; }
.av { color:#700; }
.aw { color:#C00; }
.bi { color:#509; font-weight:bold; }
.c { color:#555; background-color: black; }
.ch { color:#88F; }
.ch .k { color:#04D; }
.ch .dl { color:#039; }
.cl { color:#e9e; font-weight:bold; }
.co { color:#5ED; font-weight:bold; }
.cr { color:#0A0; }
.cv { color:#ccf; }
.df { color:#099; font-weight:bold; }
.di { color:#088; font-weight:bold; }
.dl { color:black; }
.do { color:#970; }
.ds { color:#D42; font-weight:bold; }
.e { color:#666; font-weight:bold; }
.er { color:#F00; background-color:#FAA; }
.ex { color:#F00; font-weight:bold; }
.fl { color:#60E; font-weight:bold; }
.fu { color:#5ed; font-weight:bold; }
.gv { color:#f84; }
.hx { color:#058; font-weight:bold; }
.i { color:#66f; font-weight:bold; }
.ic { color:#B44; font-weight:bold; }
.il { }
.in { color:#B2B; font-weight:bold; }
.iv { color:#aaf; }
.la { color:#970; font-weight:bold; }
.lv { color:#963; }
.oc { color:#40E; font-weight:bold; }
.of { color:#000; font-weight:bold; }
.op { }
.pc { color:#08f; font-weight:bold; }
.pd { color:#369; font-weight:bold; }
.pp { color:#579; }
.pt { color:#66f; font-weight:bold; }
.r { color:#5de; font-weight:bold; }
.r, .kw { color:#5de; font-weight:bold }
.ke { color: #808; }
.rx { background-color:#221133; }
.rx .k { color:#f8f; }
.rx .dl { color:#f0f; }
.rx .mod { color:#f0b; }
.rx .fu { color:#404; font-weight: bold; }
.s { background-color:#331122; }
.s .s { background-color:#ffe0e0; }
.s .s .s { background-color:#ffd0d0; }
.s .k { color:#F88; }
.s .dl { color:#f55; }
.sh { background-color:#f0fff0; }
.sh .k { color:#2B2; }
.sh .dl { color:#161; }
.sy { color:#Fc8; }
.sy .k { color:#Fc8; }
.sy .dl { color:#F84; }
.ta { color:#070; }
.tf { color:#070; font-weight:bold; }
.ts { color:#D70; font-weight:bold; }
.ty { color:#339; font-weight:bold; }
.v { color:#036; }
.xt { color:#444; }
.ins { background: #afa; }
.del { background: #faa; }
.chg { color: #aaf; background: #007; }
.head { color: #f8f; background: #505 }
.ins .ins { color: #080; font-weight:bold }
.del .del { color: #800; font-weight:bold }
.chg .chg { color: #66f; }
.head .head { color: #f4f; }
TOKENS
end
end
end

View File

@ -1,86 +0,0 @@
module CodeRay
class Tokens
ClassOfKind = Hash.new do |h, k|
h[k] = k.to_s
end
ClassOfKind.update with = {
:annotation => 'at',
:attribute_name => 'an',
:attribute_name_fat => 'af',
:attribute_value => 'av',
:attribute_value_fat => 'aw',
:bin => 'bi',
:char => 'ch',
:class => 'cl',
:class_variable => 'cv',
:color => 'cr',
:comment => 'c',
:complex => 'cm',
:constant => 'co',
:content => 'k',
:decorator => 'de',
:definition => 'df',
:delimiter => 'dl',
:directive => 'di',
:doc => 'do',
:doctype => 'dt',
:doc_string => 'ds',
:entity => 'en',
:error => 'er',
:escape => 'e',
:exception => 'ex',
:float => 'fl',
:function => 'fu',
:global_variable => 'gv',
:hex => 'hx',
:imaginary => 'cm',
:important => 'im',
:include => 'ic',
:inline => 'il',
:inline_delimiter => 'idl',
:instance_variable => 'iv',
:integer => 'i',
:interpreted => 'in',
:keyword => 'kw',
:key => 'ke',
:label => 'la',
:local_variable => 'lv',
:modifier => 'mod',
:oct => 'oc',
:operator_fat => 'of',
:pre_constant => 'pc',
:pre_type => 'pt',
:predefined => 'pd',
:preprocessor => 'pp',
:pseudo_class => 'ps',
:regexp => 'rx',
:reserved => 'r',
:shell => 'sh',
:string => 's',
:symbol => 'sy',
:tag => 'ta',
:tag_fat => 'tf',
:tag_special => 'ts',
:type => 'ty',
:variable => 'v',
:value => 'vl',
:xml_text => 'xt',
:insert => 'ins',
:delete => 'del',
:change => 'chg',
:head => 'head',
:ident => :NO_HIGHLIGHT, # 'id'
#:operator => 'op',
:operator => :NO_HIGHLIGHT, # 'op'
:space => :NO_HIGHLIGHT, # 'sp'
:plain => :NO_HIGHLIGHT,
}
ClassOfKind[:method] = ClassOfKind[:function]
ClassOfKind[:open] = ClassOfKind[:close] = ClassOfKind[:delimiter]
ClassOfKind[:nesting_delimiter] = ClassOfKind[:delimiter]
ClassOfKind[:escape] = ClassOfKind[:delimiter]
#ClassOfKind.default = ClassOfKind[:error] or raise 'no class found for :error!'
end
end

View File

@ -1,390 +0,0 @@
module CodeRay
# = Tokens
#
# The Tokens class represents a list of tokens returnd from
# a Scanner.
#
# A token is not a special object, just a two-element Array
# consisting of
# * the _token_ _text_ (the original source of the token in a String) or
# a _token_ _action_ (:open, :close, :begin_line, :end_line)
# * the _token_ _kind_ (a Symbol representing the type of the token)
#
# A token looks like this:
#
# ['# It looks like this', :comment]
# ['3.1415926', :float]
# ['$^', :error]
#
# Some scanners also yield sub-tokens, represented by special
# token actions, namely :open and :close.
#
# The Ruby scanner, for example, splits "a string" into:
#
# [
# [:open, :string],
# ['"', :delimiter],
# ['a string', :content],
# ['"', :delimiter],
# [:close, :string]
# ]
#
# Tokens is the interface between Scanners and Encoders:
# The input is split and saved into a Tokens object. The Encoder
# then builds the output from this object.
#
# Thus, the syntax below becomes clear:
#
# CodeRay.scan('price = 2.59', :ruby).html
# # the Tokens object is here -------^
#
# See how small it is? ;)
#
# Tokens gives you the power to handle pre-scanned code very easily:
# You can convert it to a webpage, a YAML file, or dump it into a gzip'ed string
# that you put in your DB.
#
# It also allows you to generate tokens directly (without using a scanner),
# to load them from a file, and still use any Encoder that CodeRay provides.
#
# Tokens' subclass TokenStream allows streaming to save memory.
class Tokens < Array
# The Scanner instance that created the tokens.
attr_accessor :scanner
# Whether the object is a TokenStream.
#
# Returns false.
def stream?
false
end
# Iterates over all tokens.
#
# If a filter is given, only tokens of that kind are yielded.
def each kind_filter = nil, &block
unless kind_filter
super(&block)
else
super() do |text, kind|
next unless kind == kind_filter
yield text, kind
end
end
end
# Iterates over all text tokens.
# Range tokens like [:open, :string] are left out.
#
# Example:
# tokens.each_text_token { |text, kind| text.replace html_escape(text) }
def each_text_token
each do |text, kind|
next unless text.is_a? ::String
yield text, kind
end
end
# Encode the tokens using encoder.
#
# encoder can be
# * a symbol like :html oder :statistic
# * an Encoder class
# * an Encoder object
#
# options are passed to the encoder.
def encode encoder, options = {}
unless encoder.is_a? Encoders::Encoder
unless encoder.is_a? Class
encoder_class = Encoders[encoder]
end
encoder = encoder_class.new options
end
encoder.encode_tokens self, options
end
# Turn into a string using Encoders::Text.
#
# +options+ are passed to the encoder if given.
def to_s options = {}
encode :text, options
end
# Redirects unknown methods to encoder calls.
#
# For example, if you call +tokens.html+, the HTML encoder
# is used to highlight the tokens.
def method_missing meth, options = {}
Encoders[meth].new(options).encode_tokens self
end
# Returns the tokens compressed by joining consecutive
# tokens of the same kind.
#
# This can not be undone, but should yield the same output
# in most Encoders. It basically makes the output smaller.
#
# Combined with dump, it saves space for the cost of time.
#
# If the scanner is written carefully, this is not required -
# for example, consecutive //-comment lines could already be
# joined in one comment token by the Scanner.
def optimize
last_kind = last_text = nil
new = self.class.new
for text, kind in self
if text.is_a? String
if kind == last_kind
last_text << text
else
new << [last_text, last_kind] if last_kind
last_text = text
last_kind = kind
end
else
new << [last_text, last_kind] if last_kind
last_kind = last_text = nil
new << [text, kind]
end
end
new << [last_text, last_kind] if last_kind
new
end
# Compact the object itself; see optimize.
def optimize!
replace optimize
end
# Ensure that all :open tokens have a correspondent :close one.
#
# TODO: Test this!
def fix
tokens = self.class.new
# Check token nesting using a stack of kinds.
opened = []
for type, kind in self
case type
when :open
opened.push [:close, kind]
when :begin_line
opened.push [:end_line, kind]
when :close, :end_line
expected = opened.pop
if [type, kind] != expected
# Unexpected :close; decide what to do based on the kind:
# - token was never opened: delete the :close (just skip it)
next unless opened.rindex expected
# - token was opened earlier: also close tokens in between
tokens << token until (token = opened.pop) == expected
end
end
tokens << [type, kind]
end
# Close remaining opened tokens
tokens << token while token = opened.pop
tokens
end
def fix!
replace fix
end
# TODO: Scanner#split_into_lines
#
# Makes sure that:
# - newlines are single tokens
# (which means all other token are single-line)
# - there are no open tokens at the end the line
#
# This makes it simple for encoders that work line-oriented,
# like HTML with list-style numeration.
def split_into_lines
raise NotImplementedError
end
def split_into_lines!
replace split_into_lines
end
# Dumps the object into a String that can be saved
# in files or databases.
#
# The dump is created with Marshal.dump;
# In addition, it is gzipped using GZip.gzip.
#
# The returned String object includes Undumping
# so it has an #undump method. See Tokens.load.
#
# You can configure the level of compression,
# but the default value 7 should be what you want
# in most cases as it is a good compromise between
# speed and compression rate.
#
# See GZip module.
def dump gzip_level = 7
require 'coderay/helpers/gzip_simple'
dump = Marshal.dump self
dump = dump.gzip gzip_level
dump.extend Undumping
end
# The total size of the tokens.
# Should be equal to the input size before
# scanning.
def text_size
size = 0
each_text_token do |t, k|
size + t.size
end
size
end
# Return all text tokens joined into a single string.
def text
map { |t, k| t if t.is_a? ::String }.join
end
# Include this module to give an object an #undump
# method.
#
# The string returned by Tokens.dump includes Undumping.
module Undumping
# Calls Tokens.load with itself.
def undump
Tokens.load self
end
end
# Undump the object using Marshal.load, then
# unzip it using GZip.gunzip.
#
# The result is commonly a Tokens object, but
# this is not guaranteed.
def Tokens.load dump
require 'coderay/helpers/gzip_simple'
dump = dump.gunzip
@dump = Marshal.load dump
end
end
# = TokenStream
#
# The TokenStream class is a fake Array without elements.
#
# It redirects the method << to a block given at creation.
#
# This allows scanners and Encoders to use streaming (no
# tokens are saved, the input is highlighted the same time it
# is scanned) with the same code.
#
# See CodeRay.encode_stream and CodeRay.scan_stream
class TokenStream < Tokens
# Whether the object is a TokenStream.
#
# Returns true.
def stream?
true
end
# The Array is empty, but size counts the tokens given by <<.
attr_reader :size
# Creates a new TokenStream that calls +block+ whenever
# its << method is called.
#
# Example:
#
# require 'coderay'
#
# token_stream = CodeRay::TokenStream.new do |text, kind|
# puts 'kind: %s, text size: %d.' % [kind, text.size]
# end
#
# token_stream << ['/\d+/', :regexp]
# #-> kind: rexpexp, text size: 5.
#
def initialize &block
raise ArgumentError, 'Block expected for streaming.' unless block
@callback = block
@size = 0
end
# Calls +block+ with +token+ and increments size.
#
# Returns self.
def << token
@callback.call(*token)
@size += 1
self
end
# This method is not implemented due to speed reasons. Use Tokens.
def text_size
raise NotImplementedError,
'This method is not implemented due to speed reasons.'
end
# A TokenStream cannot be dumped. Use Tokens.
def dump
raise NotImplementedError, 'A TokenStream cannot be dumped.'
end
# A TokenStream cannot be optimized. Use Tokens.
def optimize
raise NotImplementedError, 'A TokenStream cannot be optimized.'
end
end
end
if $0 == __FILE__
$VERBOSE = true
$: << File.join(File.dirname(__FILE__), '..')
eval DATA.read, nil, $0, __LINE__ + 4
end
__END__
require 'test/unit'
class TokensTest < Test::Unit::TestCase
def test_creation
assert CodeRay::Tokens < Array
tokens = nil
assert_nothing_raised do
tokens = CodeRay::Tokens.new
end
assert_kind_of Array, tokens
end
def test_adding_tokens
tokens = CodeRay::Tokens.new
assert_nothing_raised do
tokens << ['string', :type]
tokens << ['()', :operator]
end
assert_equal tokens.size, 2
end
def test_dump_undump
tokens = CodeRay::Tokens.new
assert_nothing_raised do
tokens << ['string', :type]
tokens << ['()', :operator]
end
tokens2 = nil
assert_nothing_raised do
tokens2 = tokens.dump.undump
end
assert_equal tokens, tokens2
end
end

View File

@ -1,122 +0,0 @@
require 'test/unit'
require 'coderay'
class BasicTest < Test::Unit::TestCase
def test_version
assert_nothing_raised do
assert_match(/\A\d\.\d\.\d\z/, CodeRay::VERSION)
end
end
RUBY_TEST_CODE = 'puts "Hello, World!"'
RUBY_TEST_TOKENS = [
['puts', :ident],
[' ', :space],
[:open, :string],
['"', :delimiter],
['Hello, World!', :content],
['"', :delimiter],
[:close, :string]
]
def test_simple_scan
assert_nothing_raised do
assert_equal RUBY_TEST_TOKENS, CodeRay.scan(RUBY_TEST_CODE, :ruby).to_ary
end
end
RUBY_TEST_HTML = 'puts <span class="s"><span class="dl">&quot;</span>' +
'<span class="k">Hello, World!</span><span class="dl">&quot;</span></span>'
def test_simple_highlight
assert_nothing_raised do
assert_equal RUBY_TEST_HTML, CodeRay.scan(RUBY_TEST_CODE, :ruby).html
end
end
def test_duo
assert_equal(RUBY_TEST_CODE,
CodeRay::Duo[:plain, :plain].highlight(RUBY_TEST_CODE))
assert_equal(RUBY_TEST_CODE,
CodeRay::Duo[:plain => :plain].highlight(RUBY_TEST_CODE))
end
def test_duo_stream
assert_equal(RUBY_TEST_CODE,
CodeRay::Duo[:plain, :plain].highlight(RUBY_TEST_CODE, :stream => true))
end
def test_comment_filter
assert_equal <<-EXPECTED, CodeRay.scan(<<-INPUT, :ruby).comment_filter.text
#!/usr/bin/env ruby
code
more code
EXPECTED
#!/usr/bin/env ruby
=begin
A multi-line comment.
=end
code
# A single-line comment.
more code # and another comment, in-line.
INPUT
end
def test_lines_of_code
assert_equal 2, CodeRay.scan(<<-INPUT, :ruby).lines_of_code
#!/usr/bin/env ruby
=begin
A multi-line comment.
=end
code
# A single-line comment.
more code # and another comment, in-line.
INPUT
rHTML = <<-RHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title><%= controller.controller_name.titleize %>: <%= controller.action_name %></title>
<%= stylesheet_link_tag 'scaffold' %>
</head>
<body>
<p style="color: green"><%= flash[:notice] %></p>
<div id="main">
<%= yield %>
</div>
</body>
</html>
RHTML
assert_equal 0, CodeRay.scan(rHTML, :html).lines_of_code
assert_equal 0, CodeRay.scan(rHTML, :php).lines_of_code
assert_equal 0, CodeRay.scan(rHTML, :yaml).lines_of_code
assert_equal 4, CodeRay.scan(rHTML, :rhtml).lines_of_code
end
def test_rubygems_not_loaded
assert_equal nil, defined? Gem
end if ENV['check_rubygems'] && RUBY_VERSION < '1.9'
def test_list_of_encoders
assert_kind_of(Array, CodeRay::Encoders.list)
assert CodeRay::Encoders.list.include?('count')
end
def test_list_of_scanners
assert_kind_of(Array, CodeRay::Scanners.list)
assert CodeRay::Scanners.list.include?('plaintext')
end
def test_scan_a_frozen_string
CodeRay.scan RUBY_VERSION, :ruby
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
require 'test/unit'
require 'coderay'
class PluginScannerTest < Test::Unit::TestCase
def test_load
require File.join(File.dirname(__FILE__), 'vhdl')
assert_equal 'VHDL', CodeRay.scanner(:vhdl).class.name
end
end

View File

@ -1,317 +0,0 @@
!RBIX
0
x
M
1
n
n
x
10
__script__
i
53
5
7
0
64
47
49
1
1
15
5
7
2
64
47
49
1
1
15
99
7
3
45
4
5
43
6
43
7
65
49
8
3
13
99
12
7
9
12
7
10
12
65
12
49
11
4
15
49
9
0
15
2
11
I
6
I
0
I
0
I
0
n
p
12
s
9
test/unit
x
7
require
s
7
coderay
x
17
PluginScannerTest
x
4
Test
n
x
4
Unit
x
8
TestCase
x
10
open_class
x
14
__class_init__
M
1
n
n
x
17
PluginScannerTest
i
16
5
66
99
7
0
7
1
65
67
49
2
0
49
3
4
11
I
5
I
0
I
0
I
0
n
p
4
x
9
test_load
M
1
n
n
x
9
test_load
i
48
5
45
0
1
45
0
2
65
49
3
0
49
4
1
7
5
64
49
6
2
47
49
7
1
15
5
7
8
64
45
9
10
7
11
49
12
1
49
13
0
49
14
0
47
49
15
2
11
I
4
I
0
I
0
I
0
n
p
16
x
4
File
n
n
x
11
active_path
x
7
dirname
s
4
vhdl
x
4
join
x
7
require
s
4
VHDL
x
7
CodeRay
n
x
4
vhdl
x
7
scanner
x
5
class
x
4
name
x
12
assert_equal
p
7
I
0
I
6
I
0
I
7
I
19
I
8
I
30
x
69
/Users/murphy/ruby/coderay-0.9/test/functional/load_plugin_scanner.rb
p
0
x
17
method_visibility
x
15
add_defn_method
p
3
I
2
I
6
I
10
x
69
/Users/murphy/ruby/coderay-0.9/test/functional/load_plugin_scanner.rb
p
0
x
13
attach_method
p
7
I
0
I
1
I
9
I
2
I
12
I
4
I
35
x
69
/Users/murphy/ruby/coderay-0.9/test/functional/load_plugin_scanner.rb
p
0

View File

@ -1,12 +0,0 @@
require 'test/unit'
MYDIR = File.dirname(__FILE__)
$:.unshift 'lib'
require 'coderay'
puts "Running basic CodeRay #{CodeRay::VERSION} tests..."
suite = %w(basic load_plugin_scanner word_list)
for test_case in suite
load File.join(MYDIR, test_case + '.rb')
end

View File

@ -1,322 +0,0 @@
!RBIX
0
x
M
1
n
n
x
10
__script__
i
95
5
7
0
64
47
49
1
1
15
65
7
2
45
3
4
65
49
5
0
49
6
1
49
7
2
15
99
43
8
7
9
49
10
1
7
11
64
49
12
1
15
5
7
13
64
47
49
1
1
15
5
7
14
45
15
16
43
17
47
49
18
0
7
19
63
3
47
49
20
1
15
7
21
64
7
22
64
7
23
64
35
3
19
0
15
20
0
56
24
50
25
0
15
2
11
I
6
I
2
I
0
I
0
n
p
26
s
9
test/unit
x
7
require
x
5
MYDIR
x
4
File
n
x
11
active_path
x
7
dirname
x
9
const_set
x
7
Globals
x
2
$:
x
2
[]
s
3
lib
x
2
<<
s
7
coderay
s
22
Running basic CodeRay
x
7
CodeRay
n
x
7
VERSION
x
4
to_s
s
9
tests...
x
4
puts
s
5
basic
s
19
load_plugin_scanner
s
9
word_list
M
1
p
2
x
9
for_block
t
n
x
9
__block__
i
28
57
22
1
1
15
5
45
0
1
45
2
3
21
1
1
7
4
64
81
5
49
6
2
47
49
7
1
11
I
6
I
0
I
1
I
1
n
p
8
x
4
File
n
x
5
MYDIR
n
s
3
.rb
x
1
+
x
4
join
x
4
load
p
5
I
0
I
a
I
5
I
b
I
1c
x
55
/Users/murphy/ruby/coderay-0.9/test/functional/suite.rb
p
0
x
4
each
p
15
I
0
I
1
I
9
I
3
I
1a
I
5
I
29
I
6
I
32
I
7
I
47
I
9
I
55
I
a
I
5f
x
55
/Users/murphy/ruby/coderay-0.9/test/functional/suite.rb
p
2
x
5
suite
x
9
test_case

View File

@ -1,126 +0,0 @@
class VHDL < CodeRay::Scanners::Scanner
register_for :vhdl
RESERVED_WORDS = [
'access','after','alias','all','assert','architecture','begin',
'block','body','buffer','bus','case','component','configuration','constant',
'disconnect','downto','else','elsif','end','entity','exit','file','for',
'function','generate','generic','group','guarded','if','impure','in',
'inertial','inout','is','label','library','linkage','literal','loop',
'map','new','next','null','of','on','open','others','out','package',
'port','postponed','procedure','process','pure','range','record','register',
'reject','report','return','select','severity','signal','shared','subtype',
'then','to','transport','type','unaffected','units','until','use','variable',
'wait','when','while','with','note','warning','error','failure','and',
'or','xor','not','nor',
'array'
]
PREDEFINED_TYPES = [
'bit','bit_vector','character','boolean','integer','real','time','string',
'severity_level','positive','natural','signed','unsigned','line','text',
'std_logic','std_logic_vector','std_ulogic','std_ulogic_vector','qsim_state',
'qsim_state_vector','qsim_12state','qsim_12state_vector','qsim_strength',
'mux_bit','mux_vector','reg_bit','reg_vector','wor_bit','wor_vector'
]
PREDEFINED_CONSTANTS = [
]
IDENT_KIND = CodeRay::CaseIgnoringWordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_TYPES, :pre_type).
add(PREDEFINED_CONSTANTS, :pre_constant)
ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
def scan_tokens tokens, options
state = :initial
until eos?
kind = nil
match = nil
case state
when :initial
if scan(/ \s+ | \\\n /x)
kind = :space
elsif scan(/-- .*/x)
kind = :comment
elsif scan(/ [-+*\/=<>?:;,!&^|()\[\]{}~%]+ | \.(?!\d) /x)
kind = :operator
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
kind = IDENT_KIND[match.downcase]
elsif match = scan(/[a-z]?"/i)
tokens << [:open, :string]
state = :string
kind = :delimiter
elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
kind = :char
elsif scan(/(?:\d+)(?![.eEfF])/)
kind = :integer
elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
kind = :float
else
getch
kind = :error
end
when :string
if scan(/[^\\\n"]+/)
kind = :content
elsif scan(/"/)
tokens << ['"', :delimiter]
tokens << [:close, :string]
state = :initial
next
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
kind = :char
elsif scan(/ \\ | $ /x)
tokens << [:close, :string]
kind = :error
state = :initial
else
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
end
else
raise_inspect 'Unknown state', tokens
end
match ||= matched
if $DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens
end
raise_inspect 'Empty token', tokens unless match
tokens << [match, kind]
end
if state == :string
tokens << [:close, :string]
end
tokens
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,79 +0,0 @@
require 'test/unit'
require 'coderay'
class WordListTest < Test::Unit::TestCase
include CodeRay
# define word arrays
RESERVED_WORDS = %w[
asm break case continue default do else
...
]
PREDEFINED_TYPES = %w[
int long short char void
...
]
PREDEFINED_CONSTANTS = %w[
EOF NULL ...
]
# make a WordList
IDENT_KIND = WordList.new(:ident).
add(RESERVED_WORDS, :reserved).
add(PREDEFINED_TYPES, :pre_type).
add(PREDEFINED_CONSTANTS, :pre_constant)
def test_word_list_example
assert_equal :pre_type, IDENT_KIND['void']
# assert_equal :pre_constant, IDENT_KIND['...'] # not specified
end
def test_word_list
list = WordList.new(:ident).add(['foobar'], :reserved)
assert_equal :reserved, list['foobar']
assert_equal :ident, list['FooBar']
end
def test_word_list_cached
list = WordList.new(:ident, true).add(['foobar'], :reserved)
assert_equal :reserved, list['foobar']
assert_equal :ident, list['FooBar']
end
def test_case_ignoring_word_list
list = CaseIgnoringWordList.new(:ident).add(['foobar'], :reserved)
assert_equal :ident, list['foo']
assert_equal :reserved, list['foobar']
assert_equal :reserved, list['FooBar']
list = CaseIgnoringWordList.new(:ident).add(['FooBar'], :reserved)
assert_equal :ident, list['foo']
assert_equal :reserved, list['foobar']
assert_equal :reserved, list['FooBar']
end
def test_case_ignoring_word_list_cached
list = CaseIgnoringWordList.new(:ident, true).add(['foobar'], :reserved)
assert_equal :ident, list['foo']
assert_equal :reserved, list['foobar']
assert_equal :reserved, list['FooBar']
list = CaseIgnoringWordList.new(:ident, true).add(['FooBar'], :reserved)
assert_equal :ident, list['foo']
assert_equal :reserved, list['foobar']
assert_equal :reserved, list['FooBar']
end
def test_dup
list = WordList.new(:ident).add(['foobar'], :reserved)
assert_equal :reserved, list['foobar']
list2 = list.dup
list2.add(%w[foobar], :keyword)
assert_equal :keyword, list2['foobar']
assert_equal :reserved, list['foobar']
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +1,45 @@
= CodeRay
[- Tired of blue'n'gray? Try the original version of this documentation on
coderay.rubychan.de[http://coderay.rubychan.de/doc/] (use Ctrl+Click to open it in its own frame.) -]
Tired of blue'n'gray? Try the original version of this documentation on
coderay.rubychan.de[http://coderay.rubychan.de/doc/] :-)
== About
CodeRay is a Ruby library for syntax highlighting.
Syntax highlighting means: You put your code in, and you get it back colored;
Keywords, strings, floats, comments - all in different colors.
And with line numbers.
You put your code in, and you get it back colored; Keywords, strings,
floats, comments - all in different colors. And with line numbers.
*Syntax* *Highlighting*...
* makes code easier to read and maintain
* lets you detect syntax errors faster
* helps you to understand the syntax of a language
* looks nice
* is what everybody should have on their website
* is what everybody wants to have on their website
* solves all your problems and makes the girls run after you
Version: 0.9.7
Author:: murphy (Kornelius Kalnbach)
Contact:: murphy rubychan de
Website:: coderay.rubychan.de[http://coderay.rubychan.de]
License:: GNU LGPL; see LICENSE file in the main directory.
== Installation
You need RubyGems[http://rubyforge.org/frs/?group_id=126].
% gem install coderay
=== Dependencies
CodeRay needs Ruby 1.8.6 or later. It also runs with Ruby 1.9.1+ and JRuby 1.1+.
CodeRay needs Ruby 1.8.7+ or 1.9.2+. It also runs on Rubinius and JRuby.
== Example Usage
(Forgive me, but this is not highlighted.)
require 'coderay'
tokens = CodeRay.scan "puts 'Hello, world!'", :ruby
page = tokens.html :line_numbers => :inline, :wrap => :page
puts page
html = CodeRay.scan("puts 'Hello, world!'", :ruby).div(:line_numbers => :table)
== Documentation
See CodeRay.
Please report errors in this documentation to <murphy rubychan de>.
== Credits
@ -94,7 +82,6 @@ Please report errors in this documentation to <murphy rubychan de>.
* Rob Aldred for the terminal encoder
* Trans for pointing out $DEBUG dependencies
* Flameeyes for finding that Term::ANSIColor was obsolete
* Etienne Massip for reporting a serious bug in JavaScript scanner
* matz and all Ruby gods and gurus
* The inventors of: the computer, the internet, the true color display, HTML &
CSS, VIM, Ruby, pizza, microwaves, guitars, scouting, programming, anime,
@ -124,6 +111,8 @@ Where would we be without all those people?
less useless
* Term::ANSIColor[http://term-ansicolor.rubyforge.org/]
* PLEAC[http://pleac.sourceforge.net/] code examples
* Github
* Travis CI (http://travis-ci.org/rubychan/github)
=== Free

View File

@ -1,8 +1,7 @@
require 'rake/rdoctask'
$:.unshift File.dirname(__FILE__) unless $:.include? '.'
ROOT = '.'
LIB_ROOT = File.join ROOT, 'lib'
EXTRA_RDOC_FILES = %w(lib/README FOLDERS)
task :default => :test
@ -15,20 +14,21 @@ if File.directory? 'rake_tasks'
else
# fallback tasks when rake_tasks folder is not present
# fallback tasks when rake_tasks folder is not present (eg. in the distribution package)
desc 'Run CodeRay tests (basic)'
task :test do
ruby './test/functional/suite.rb'
ruby './test/functional/for_redcloth.rb'
end
gem 'rdoc' if defined? gem
require 'rdoc/task'
desc 'Generate documentation for CodeRay'
Rake::RDocTask.new :doc do |rd|
rd.title = 'CodeRay Documentation'
rd.main = 'lib/README'
rd.main = 'README_INDEX.rdoc'
rd.rdoc_files.add Dir['lib']
rd.rdoc_files.add 'lib/README'
rd.rdoc_files.add 'FOLDERS'
rd.rdoc_files.add rd.main
rd.rdoc_dir = 'doc'
end

View File

@ -21,10 +21,7 @@ module CodeRay
# Create a new Duo, holding a lang and a format to highlight code.
#
# simple:
# CodeRay::Duo[:ruby, :page].highlight 'bla 42'
#
# streaming:
# CodeRay::Duo[:ruby, :page].highlight 'bar 23', :stream => true
# CodeRay::Duo[:ruby, :html].highlight 'bla 42'
#
# with options:
# CodeRay::Duo[:ruby, :html, :hint => :debug].highlight '????::??'
@ -38,7 +35,7 @@ module CodeRay
# The options are forwarded to scanner and encoder
# (see CodeRay.get_scanner_options).
def initialize lang = nil, format = nil, options = {}
if format == nil and lang.is_a? Hash and lang.size == 1
if format.nil? && lang.is_a?(Hash) && lang.size == 1
@lang = lang.keys.first
@format = lang[@lang]
else
@ -64,22 +61,21 @@ module CodeRay
end
# Tokenize and highlight the code using +scanner+ and +encoder+.
#
# If the :stream option is set, the Duo will go into streaming mode,
# saving memory for the cost of time.
def encode code, options = { :stream => false }
stream = options.delete :stream
def encode code, options = {}
options = @options.merge options
if stream
encoder.encode_stream(code, @lang, options)
else
scanner.code = code
encoder.encode_tokens(scanner.tokenize, options)
end
encoder.encode(code, @lang, options)
end
alias highlight encode
# Allows to use Duo like a proc object:
#
# CodeRay::Duo[:python => :yaml].call(code)
#
# or, in Ruby 1.9 and later:
#
# CodeRay::Duo[:python => :yaml].(code)
alias call encode
end
end

View File

@ -8,6 +8,7 @@ module CodeRay
# mechanism and the [] method that returns the Encoder class
# belonging to the given format.
module Encoders
extend PluginHost
plugin_path File.dirname(__FILE__), 'encoders'
@ -27,32 +28,30 @@ module CodeRay
extend Plugin
plugin_host Encoders
attr_reader :token_stream
class << self
# Returns if the Encoder can be used in streaming mode.
def streamable?
is_a? Streamable
end
# If FILE_EXTENSION isn't defined, this method returns the
# downcase class name instead.
def const_missing sym
if sym == :FILE_EXTENSION
plugin_id
(defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s
else
super
end
end
# The default file extension for output file of this encoder class.
def file_extension
self::FILE_EXTENSION
end
end
# Subclasses are to store their default options in this constant.
DEFAULT_OPTIONS = { :stream => false }
DEFAULT_OPTIONS = { }
# The options you gave the Encoder at creating.
attr_accessor :options
attr_accessor :options, :scanner
# Creates a new Encoder.
# +options+ is saved and used for all encode operations, as long
@ -61,57 +60,90 @@ module CodeRay
# Encoder objects provide three encode methods:
# - encode simply takes a +code+ string and a +lang+
# - encode_tokens expects a +tokens+ object instead
# - encode_stream is like encode, but uses streaming mode.
#
# Each method has an optional +options+ parameter. These are
# added to the options you passed at creation.
def initialize options = {}
@options = self.class::DEFAULT_OPTIONS.merge options
raise "I am only the basic Encoder class. I can't encode "\
"anything. :( Use my subclasses." if self.class == Encoder
@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false
end
# Encode a Tokens object.
def encode_tokens tokens, options = {}
options = @options.merge options
@scanner = tokens.scanner if tokens.respond_to? :scanner
setup options
compile tokens, options
finish options
end
# Encode the given +code+ after tokenizing it using the Scanner
# for +lang+.
# Encode the given +code+ using the Scanner for +lang+.
def encode code, lang, options = {}
options = @options.merge options
scanner_options = CodeRay.get_scanner_options(options)
tokens = CodeRay.scan code, lang, scanner_options
encode_tokens tokens, options
@scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self)
setup options
@scanner.tokenize
finish options
end
# You can use highlight instead of encode, if that seems
# more clear to you.
alias highlight encode
# Encode the given +code+ using the Scanner for +lang+ in
# streaming mode.
def encode_stream code, lang, options = {}
raise NotStreamableError, self unless kind_of? Streamable
options = @options.merge options
setup options
scanner_options = CodeRay.get_scanner_options options
@token_stream =
CodeRay.scan_stream code, lang, scanner_options, &self
finish options
end
# Behave like a proc. The token method is converted to a proc.
def to_proc
method(:token).to_proc
end
# Return the default file extension for outputs of this encoder.
# The default file extension for this encoder.
def file_extension
self.class::FILE_EXTENSION
self.class.file_extension
end
def << token
unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN
warn 'Using old Tokens#<< interface.'
@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true
end
self.token(*token)
end
# Called with +content+ and +kind+ of the currently scanned token.
# For simple scanners, it's enougth to implement this method.
#
# By default, it calls text_token, begin_group, end_group, begin_line,
# or end_line, depending on the +content+.
def token content, kind
case content
when String
text_token content, kind
when :begin_group
begin_group kind
when :end_group
end_group kind
when :begin_line
begin_line kind
when :end_line
end_line kind
else
raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind]
end
end
# Called for each text token ([text, kind]), where text is a String.
def text_token text, kind
@out << text
end
# Starts a token group with the given +kind+.
def begin_group kind
end
# Ends a token group with the given +kind+.
def end_group kind
end
# Starts a new line token group with the given +kind+.
def begin_line kind
end
# Ends a new line token group with the given +kind+.
def end_line kind
end
protected
@ -121,68 +153,17 @@ module CodeRay
#
# See the HTML Encoder for an example of option caching.
def setup options
@out = ''
@out = get_output(options)
end
# Called with +content+ and +kind+ of the currently scanned token.
# For simple scanners, it's enougth to implement this method.
#
# By default, it calls text_token or block_token, depending on
# whether +content+ is a String.
def token content, kind
encoded_token =
if content.is_a? ::String
text_token content, kind
elsif content.is_a? ::Symbol
block_token content, kind
else
raise 'Unknown token content type: %p' % [content]
end
append_encoded_token_to_output encoded_token
def get_output options
options[:out] || ''
end
def append_encoded_token_to_output encoded_token
@out << encoded_token if encoded_token && defined?(@out) && @out
end
# Called for each text token ([text, kind]), where text is a String.
def text_token text, kind
end
# Called for each block (non-text) token ([action, kind]),
# where +action+ is a Symbol.
#
# Calls open_token, close_token, begin_line, and end_line according to
# the value of +action+.
def block_token action, kind
case action
when :open
open_token kind
when :close
close_token kind
when :begin_line
begin_line kind
when :end_line
end_line kind
else
raise 'unknown block action: %p' % action
end
end
# Called for each block token at the start of the block ([:open, kind]).
def open_token kind
end
# Called for each block token end of the block ([:close, kind]).
def close_token kind
end
# Called for each line token block at the start of the line ([:begin_line, kind]).
def begin_line kind
end
# Called for each line token block at the end of the line ([:end_line, kind]).
def end_line kind
# Append data.to_s to the output. Returns the argument.
def output data
@out << data.to_s
data
end
# Called with merged options after encoding starts.
@ -193,19 +174,26 @@ module CodeRay
# Do the encoding.
#
# The already created +tokens+ object must be used; it can be a
# TokenStream or a Tokens object.
if RUBY_VERSION >= '1.9'
def compile tokens, options
for text, kind in tokens
token text, kind
end
# The already created +tokens+ object must be used; it must be a
# Tokens object.
def compile tokens, options = {}
content = nil
for item in tokens
if item.is_a? Array
raise ArgumentError, 'Two-element array tokens are no longer supported.'
end
if content
token content, item
content = nil
else
def compile tokens, options
tokens.each(&self)
content = item
end
end
raise 'odd number list for Tokens' if content
end
alias tokens compile
public :tokens
end

View File

@ -0,0 +1,17 @@
module CodeRay
module Encoders
map \
:loc => :lines_of_code,
:plain => :text,
:plaintext => :text,
:remove_comments => :comment_filter,
:stats => :statistic,
:term => :terminal,
:tty => :terminal,
:yml => :yaml
# No default because Tokens#nonsense should raise NoMethodError.
end
end

View File

@ -0,0 +1,25 @@
module CodeRay
module Encoders
load :token_kind_filter
# A simple Filter that removes all tokens of the :comment kind.
#
# Alias: +remove_comments+
#
# Usage:
# CodeRay.scan('print # foo', :ruby).comment_filter.text
# #-> "print "
#
# See also: TokenKindFilter, LinesOfCode
class CommentFilter < TokenKindFilter
register_for :comment_filter
DEFAULT_OPTIONS = superclass::DEFAULT_OPTIONS.merge \
:exclude => [:comment, :docstring]
end
end
end

View File

@ -0,0 +1,39 @@
module CodeRay
module Encoders
# Returns the number of tokens.
#
# Text and block tokens are counted.
class Count < Encoder
register_for :count
protected
def setup options
super
@count = 0
end
def finish options
output @count
end
public
def text_token text, kind
@count += 1
end
def begin_group kind
@count += 1
end
alias end_group begin_group
alias begin_line begin_group
alias end_line begin_group
end
end
end

View File

@ -10,37 +10,49 @@ module Encoders
# You cannot fully restore the tokens information from the
# output, because consecutive :space tokens are merged.
# Use Tokens#dump for caching purposes.
#
# See also: Scanners::Debug
class Debug < Encoder
include Streamable
register_for :debug
FILE_EXTENSION = 'raydebug'
protected
def initialize options = {}
super
@opened = []
end
def text_token text, kind
if kind == :space
text
@out << text
else
# TODO: Escape (
text = text.gsub(/[)\\]/, '\\\\\0') # escape ) and \
"#{kind}(#{text})"
@out << kind.to_s << '(' << text << ')'
end
end
def open_token kind
"#{kind}<"
def begin_group kind
@opened << kind
@out << kind.to_s << '<'
end
def close_token kind
">"
def end_group kind
if @opened.last != kind
puts @out
raise "we are inside #{@opened.inspect}, not #{kind}"
end
@opened.pop
@out << '>'
end
def begin_line kind
"#{kind}["
@out << kind.to_s << '['
end
def end_line kind
"]"
@out << ']'
end
end

View File

@ -0,0 +1,23 @@
module CodeRay
module Encoders
load :html
# Wraps HTML output into a DIV element, using inline styles by default.
#
# See Encoders::HTML for available options.
class Div < HTML
FILE_EXTENSION = 'div.html'
register_for :div
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \
:css => :style,
:wrap => :div,
:line_numbers => false
end
end
end

View File

@ -0,0 +1,58 @@
module CodeRay
module Encoders
# A Filter encoder has another Tokens instance as output.
# It can be subclass to select, remove, or modify tokens in the stream.
#
# Subclasses of Filter are called "Filters" and can be chained.
#
# == Options
#
# === :tokens
#
# The Tokens object which will receive the output.
#
# Default: Tokens.new
#
# See also: TokenKindFilter
class Filter < Encoder
register_for :filter
protected
def setup options
super
@tokens = options[:tokens] || Tokens.new
end
def finish options
output @tokens
end
public
def text_token text, kind # :nodoc:
@tokens.text_token text, kind
end
def begin_group kind # :nodoc:
@tokens.begin_group kind
end
def begin_line kind # :nodoc:
@tokens.begin_line kind
end
def end_group kind # :nodoc:
@tokens.end_group kind
end
def end_line kind # :nodoc:
@tokens.end_line kind
end
end
end
end

View File

@ -0,0 +1,302 @@
require 'set'
module CodeRay
module Encoders
# = HTML Encoder
#
# This is CodeRay's most important highlighter:
# It provides save, fast XHTML generation and CSS support.
#
# == Usage
#
# require 'coderay'
# puts CodeRay.scan('Some /code/', :ruby).html #-> a HTML page
# puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span)
# #-> <span class="CodeRay"><span class="co">Some</span> /code/</span>
# puts CodeRay.scan('Some /code/', :ruby).span #-> the same
#
# puts CodeRay.scan('Some code', :ruby).html(
# :wrap => nil,
# :line_numbers => :inline,
# :css => :style
# )
#
# == Options
#
# === :tab_width
# Convert \t characters to +n+ spaces (a number.)
#
# Default: 8
#
# === :css
# How to include the styles; can be :class or :style.
#
# Default: :class
#
# === :wrap
# Wrap in :page, :div, :span or nil.
#
# You can also use Encoders::Div and Encoders::Span.
#
# Default: nil
#
# === :title
#
# The title of the HTML page (works only when :wrap is set to :page.)
#
# Default: 'CodeRay output'
#
# === :line_numbers
# Include line numbers in :table, :inline, or nil (no line numbers)
#
# Default: nil
#
# === :line_number_anchors
# Adds anchors and links to the line numbers. Can be false (off), true (on),
# or a prefix string that will be prepended to the anchor name.
#
# The prefix must consist only of letters, digits, and underscores.
#
# Default: true, default prefix name: "line"
#
# === :line_number_start
# Where to start with line number counting.
#
# Default: 1
#
# === :bold_every
# Make every +n+-th number appear bold.
#
# Default: 10
#
# === :highlight_lines
#
# Highlights certain line numbers.
# Can be any Enumerable, typically just an Array or Range, of numbers.
#
# Bolding is deactivated when :highlight_lines is set. It only makes sense
# in combination with :line_numbers.
#
# Default: nil
#
# === :hint
# Include some information into the output using the title attribute.
# Can be :info (show token kind on mouse-over), :info_long (with full path)
# or :debug (via inspect).
#
# Default: false
class HTML < Encoder
register_for :html
FILE_EXTENSION = 'snippet.html'
DEFAULT_OPTIONS = {
:tab_width => 8,
:css => :class,
:style => :alpha,
:wrap => nil,
:title => 'CodeRay output',
:line_numbers => nil,
:line_number_anchors => 'n',
:line_number_start => 1,
:bold_every => 10,
:highlight_lines => nil,
:hint => false,
}
autoload :Output, 'coderay/encoders/html/output'
autoload :CSS, 'coderay/encoders/html/css'
autoload :Numbering, 'coderay/encoders/html/numbering'
attr_reader :css
protected
HTML_ESCAPE = { #:nodoc:
'&' => '&amp;',
'"' => '&quot;',
'>' => '&gt;',
'<' => '&lt;',
}
# This was to prevent illegal HTML.
# Strange chars should still be avoided in codes.
evil_chars = Array(0x00...0x20) - [?\n, ?\t, ?\s]
evil_chars.each { |i| HTML_ESCAPE[i.chr] = ' ' }
#ansi_chars = Array(0x7f..0xff)
#ansi_chars.each { |i| HTML_ESCAPE[i.chr] = '&#%d;' % i }
# \x9 (\t) and \xA (\n) not included
#HTML_ESCAPE_PATTERN = /[\t&"><\0-\x8\xB-\x1f\x7f-\xff]/
HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1f]/
TOKEN_KIND_TO_INFO = Hash.new do |h, kind|
h[kind] = kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize }
end
TRANSPARENT_TOKEN_KINDS = Set[
:delimiter, :modifier, :content, :escape, :inline_delimiter,
]
# Generate a hint about the given +kinds+ in a +hint+ style.
#
# +hint+ may be :info, :info_long or :debug.
def self.token_path_to_hint hint, kinds
kinds = Array kinds
title =
case hint
when :info
kinds = kinds[1..-1] if TRANSPARENT_TOKEN_KINDS.include? kinds.first
TOKEN_KIND_TO_INFO[kinds.first]
when :info_long
kinds.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/')
when :debug
kinds.inspect
end
title ? " title=\"#{title}\"" : ''
end
def setup options
super
if options[:wrap] || options[:line_numbers]
@real_out = @out
@out = ''
end
@HTML_ESCAPE = HTML_ESCAPE.dup
@HTML_ESCAPE["\t"] = ' ' * options[:tab_width]
@opened = []
@last_opened = nil
@css = CSS.new options[:style]
hint = options[:hint]
if hint && ![:debug, :info, :info_long].include?(hint)
raise ArgumentError, "Unknown value %p for :hint; \
expected :info, :info_long, :debug, false, or nil." % hint
end
css_classes = TokenKinds
case options[:css]
when :class
@span_for_kind = Hash.new do |h, k|
if k.is_a? ::Symbol
kind = k_dup = k
else
kind = k.first
k_dup = k.dup
end
if kind != :space && (hint || css_class = css_classes[kind])
title = HTML.token_path_to_hint hint, k if hint
css_class ||= css_classes[kind]
h[k_dup] = "<span#{title}#{" class=\"#{css_class}\"" if css_class}>"
else
h[k_dup] = nil
end
end
when :style
@span_for_kind = Hash.new do |h, k|
kind = k.is_a?(Symbol) ? k : k.first
h[k.is_a?(Symbol) ? k : k.dup] =
if kind != :space && (hint || css_classes[kind])
title = HTML.token_path_to_hint hint, k if hint
style = @css.get_style Array(k).map { |c| css_classes[c] }
"<span#{title}#{" style=\"#{style}\"" if style}>"
end
end
else
raise ArgumentError, "Unknown value %p for :css." % options[:css]
end
@set_last_opened = options[:hint] || options[:css] == :style
end
def finish options
unless @opened.empty?
warn '%d tokens still open: %p' % [@opened.size, @opened] if $CODERAY_DEBUG
@out << '</span>' while @opened.pop
@last_opened = nil
end
@out.extend Output
@out.css = @css
if options[:line_numbers]
Numbering.number! @out, options[:line_numbers], options
end
@out.wrap! options[:wrap]
@out.apply_title! options[:title]
if defined?(@real_out) && @real_out
@real_out << @out
@out = @real_out
end
super
end
public
def text_token text, kind
if text =~ /#{HTML_ESCAPE_PATTERN}/o
text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] }
end
if style = @span_for_kind[@last_opened ? [kind, *@opened] : kind]
@out << style << text << '</span>'
else
@out << text
end
end
# token groups, eg. strings
def begin_group kind
@out << (@span_for_kind[@last_opened ? [kind, *@opened] : kind] || '<span>')
@opened << kind
@last_opened = kind if @set_last_opened
end
def end_group kind
if $CODERAY_DEBUG && (@opened.empty? || @opened.last != kind)
warn 'Malformed token stream: Trying to close a token (%p) ' \
'that is not open. Open are: %p.' % [kind, @opened[1..-1]]
end
if @opened.pop
@out << '</span>'
@last_opened = @opened.last if @last_opened
end
end
# whole lines to be highlighted, eg. a deleted line in a diff
def begin_line kind
if style = @span_for_kind[@last_opened ? [kind, *@opened] : kind]
if style['class="']
@out << style.sub('class="', 'class="line ')
else
@out << style.sub('>', ' class="line">')
end
else
@out << '<span class="line">'
end
@opened << kind
@last_opened = kind if @options[:css] == :style
end
def end_line kind
if $CODERAY_DEBUG && (@opened.empty? || @opened.last != kind)
warn 'Malformed token stream: Trying to close a line (%p) ' \
'that is not open. Open are: %p.' % [kind, @opened[1..-1]]
end
if @opened.pop
@out << '</span>'
@last_opened = @opened.last if @last_opened
end
end
end
end
end

View File

@ -0,0 +1,83 @@
module CodeRay
module Encoders
# A simple JSON Encoder.
#
# Example:
# CodeRay.scan('puts "Hello world!"', :ruby).json
# yields
# [
# {"type"=>"text", "text"=>"puts", "kind"=>"ident"},
# {"type"=>"text", "text"=>" ", "kind"=>"space"},
# {"type"=>"block", "action"=>"open", "kind"=>"string"},
# {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
# {"type"=>"text", "text"=>"Hello world!", "kind"=>"content"},
# {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
# {"type"=>"block", "action"=>"close", "kind"=>"string"},
# ]
class JSON < Encoder
begin
require 'json'
rescue LoadError
begin
require 'rubygems' unless defined? Gem
gem 'json'
require 'json'
rescue LoadError
$stderr.puts "The JSON encoder needs the JSON library.\n" \
"Please gem install json."
raise
end
end
register_for :json
FILE_EXTENSION = 'json'
protected
def setup options
super
@first = true
@out << '['
end
def finish options
@out << ']'
end
def append data
if @first
@first = false
else
@out << ','
end
@out << data.to_json
end
public
def text_token text, kind
append :type => 'text', :text => text, :kind => kind
end
def begin_group kind
append :type => 'block', :action => 'open', :kind => kind
end
def end_group kind
append :type => 'block', :action => 'close', :kind => kind
end
def begin_line kind
append :type => 'block', :action => 'begin_line', :kind => kind
end
def end_line kind
append :type => 'block', :action => 'end_line', :kind => kind
end
end
end
end

View File

@ -0,0 +1,45 @@
module CodeRay
module Encoders
# Counts the LoC (Lines of Code). Returns an Integer >= 0.
#
# Alias: +loc+
#
# Everything that is not comment, markup, doctype/shebang, or an empty line,
# is considered to be code.
#
# For example,
# * HTML files not containing JavaScript have 0 LoC
# * in a Java class without comments, LoC is the number of non-empty lines
#
# A Scanner class should define the token kinds that are not code in the
# KINDS_NOT_LOC constant, which defaults to [:comment, :doctype].
class LinesOfCode < TokenKindFilter
register_for :lines_of_code
NON_EMPTY_LINE = /^\s*\S.*$/
protected
def setup options
if scanner
kinds_not_loc = scanner.class::KINDS_NOT_LOC
else
warn "Tokens have no associated scanner, counting all nonempty lines." if $VERBOSE
kinds_not_loc = CodeRay::Scanners::Scanner::KINDS_NOT_LOC
end
options[:exclude] = kinds_not_loc
super options
end
def finish options
output @tokens.text.scan(NON_EMPTY_LINE).size
end
end
end
end

View File

@ -6,17 +6,9 @@ module Encoders
# Does nothing and returns an empty string.
class Null < Encoder
include Streamable
register_for :null
# Defined for faster processing
def to_proc
proc {}
end
protected
def token(*)
def text_token text, kind
# do nothing
end

View File

@ -0,0 +1,24 @@
module CodeRay
module Encoders
load :html
# Wraps the output into a HTML page, using CSS classes and
# line numbers in the table format by default.
#
# See Encoders::HTML for available options.
class Page < HTML
FILE_EXTENSION = 'html'
register_for :page
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \
:css => :class,
:wrap => :page,
:line_numbers => :table
end
end
end

View File

@ -0,0 +1,23 @@
module CodeRay
module Encoders
load :html
# Wraps HTML output into a SPAN element, using inline styles by default.
#
# See Encoders::HTML for available options.
class Span < HTML
FILE_EXTENSION = 'span.html'
register_for :span
DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge \
:css => :style,
:wrap => :span,
:line_numbers => false
end
end
end

View File

@ -2,42 +2,26 @@ module CodeRay
module Encoders
# Makes a statistic for the given tokens.
#
# Alias: +stats+
class Statistic < Encoder
include Streamable
register_for :stats, :statistic
register_for :statistic
attr_reader :type_stats, :real_token_count
attr_reader :type_stats, :real_token_count # :nodoc:
TypeStats = Struct.new :count, :size # :nodoc:
protected
TypeStats = Struct.new :count, :size
def setup options
super
@type_stats = Hash.new { |h, k| h[k] = TypeStats.new 0, 0 }
@real_token_count = 0
end
def generate tokens, options
@tokens = tokens
super
end
def text_token text, kind
@real_token_count += 1 unless kind == :space
@type_stats[kind].count += 1
@type_stats[kind].size += text.size
@type_stats['TOTAL'].size += text.size
@type_stats['TOTAL'].count += 1
end
# TODO Hierarchy handling
def block_token action, kind
@type_stats['TOTAL'].count += 1
@type_stats['open/close'].count += 1
end
STATS = <<-STATS
STATS = <<-STATS # :nodoc:
Code Statistics
@ -50,8 +34,8 @@ Token Types (%d):
-------------------------------------------------------------
%s
STATS
# space 12007 33.81 % 1.7
TOKEN_TYPES_ROW = <<-TKR
TOKEN_TYPES_ROW = <<-TKR # :nodoc:
%-20s %8d %6.2f %% %5.1f
TKR
@ -64,11 +48,46 @@ Token Types (%d):
types_stats = @type_stats.sort_by { |k, v| [-v.count, k.to_s] }.map do |k, v|
TOKEN_TYPES_ROW % [k, v.count, 100.0 * v.count / all_count, v.size]
end.join
STATS % [
@out << STATS % [
all_count, @real_token_count, all_size,
@type_stats.delete_if { |k, v| k.is_a? String }.size,
types_stats
]
super
end
public
def text_token text, kind
@real_token_count += 1 unless kind == :space
@type_stats[kind].count += 1
@type_stats[kind].size += text.size
@type_stats['TOTAL'].size += text.size
@type_stats['TOTAL'].count += 1
end
# TODO Hierarchy handling
def begin_group kind
block_token ':begin_group', kind
end
def end_group kind
block_token ':end_group', kind
end
def begin_line kind
block_token ':begin_line', kind
end
def end_line kind
block_token ':end_line', kind
end
def block_token action, kind
@type_stats['TOTAL'].count += 1
@type_stats[action].count += 1
@type_stats[kind].count += 1
end
end

View File

@ -0,0 +1,179 @@
module CodeRay
module Encoders
# Outputs code highlighted for a color terminal.
#
# Note: This encoder is in beta. It currently doesn't use the Styles.
#
# Alias: +term+
#
# == Authors & License
#
# By Rob Aldred (http://robaldred.co.uk)
#
# Based on idea by Nathan Weizenbaum (http://nex-3.com)
#
# MIT License (http://www.opensource.org/licenses/mit-license.php)
class Terminal < Encoder
register_for :terminal
TOKEN_COLORS = {
:annotation => '35',
:attribute_name => '33',
:attribute_value => '31',
:binary => '1;35',
:char => {
:self => '36', :delimiter => '34'
},
:class => '1;35',
:class_variable => '36',
:color => '32',
:comment => '37',
:complex => '34',
:constant => ['34', '4'],
:decoration => '35',
:definition => '1;32',
:directive => ['32', '4'],
:doc => '46',
:doctype => '1;30',
:doc_string => ['31', '4'],
:entity => '33',
:error => ['1;33', '41'],
:exception => '1;31',
:float => '1;35',
:function => '1;34',
:global_variable => '42',
:hex => '1;36',
:include => '33',
:integer => '1;34',
:key => '35',
:label => '1;15',
:local_variable => '33',
:octal => '1;35',
:operator_name => '1;29',
:predefined_constant => '1;36',
:predefined_type => '1;30',
:predefined => ['4', '1;34'],
:preprocessor => '36',
:pseudo_class => '34',
:regexp => {
:self => '31',
:content => '31',
:delimiter => '1;29',
:modifier => '35',
:function => '1;29'
},
:reserved => '1;31',
:shell => {
:self => '42',
:content => '1;29',
:delimiter => '37',
},
:string => {
:self => '32',
:modifier => '1;32',
:escape => '1;36',
:delimiter => '1;32',
},
:symbol => '1;32',
:tag => '34',
:type => '1;34',
:value => '36',
:variable => '34',
:insert => '42',
:delete => '41',
:change => '44',
:head => '45'
}
TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved]
TOKEN_COLORS[:method] = TOKEN_COLORS[:function]
TOKEN_COLORS[:imaginary] = TOKEN_COLORS[:complex]
TOKEN_COLORS[:begin_group] = TOKEN_COLORS[:end_group] =
TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter]
protected
def setup(options)
super
@opened = []
@subcolors = nil
end
public
def text_token text, kind
if color = (@subcolors || TOKEN_COLORS)[kind]
if Hash === color
if color[:self]
color = color[:self]
else
@out << text
return
end
end
@out << ansi_colorize(color)
@out << text.gsub("\n", ansi_clear + "\n" + ansi_colorize(color))
@out << ansi_clear
@out << ansi_colorize(@subcolors[:self]) if @subcolors && @subcolors[:self]
else
@out << text
end
end
def begin_group kind
@opened << kind
@out << open_token(kind)
end
alias begin_line begin_group
def end_group kind
if @opened.empty?
# nothing to close
else
@opened.pop
@out << ansi_clear
@out << open_token(@opened.last)
end
end
def end_line kind
if @opened.empty?
# nothing to close
else
@opened.pop
# whole lines to be highlighted,
# eg. added/modified/deleted lines in a diff
@out << "\t" * 100 + ansi_clear
@out << open_token(@opened.last)
end
end
private
def open_token kind
if color = TOKEN_COLORS[kind]
if Hash === color
@subcolors = color
ansi_colorize(color[:self]) if color[:self]
else
@subcolors = {}
ansi_colorize(color)
end
else
@subcolors = nil
''
end
end
def ansi_colorize(color)
Array(color).map { |c| "\e[#{c}m" }.join
end
def ansi_clear
ansi_colorize(0)
end
end
end
end

View File

@ -0,0 +1,46 @@
module CodeRay
module Encoders
# Concats the tokens into a single string, resulting in the original
# code string if no tokens were removed.
#
# Alias: +plain+, +plaintext+
#
# == Options
#
# === :separator
# A separator string to join the tokens.
#
# Default: empty String
class Text < Encoder
register_for :text
FILE_EXTENSION = 'txt'
DEFAULT_OPTIONS = {
:separator => nil
}
def text_token text, kind
super
if @first
@first = false
else
@out << @sep
end if @sep
end
protected
def setup options
super
@first = true
@sep = options[:separator]
end
end
end
end

View File

@ -0,0 +1,111 @@
module CodeRay
module Encoders
load :filter
# A Filter that selects tokens based on their token kind.
#
# == Options
#
# === :exclude
#
# One or many symbols (in an Array) which shall be excluded.
#
# Default: []
#
# === :include
#
# One or many symbols (in an array) which shall be included.
#
# Default: :all, which means all tokens are included.
#
# Exclusion wins over inclusion.
#
# See also: CommentFilter
class TokenKindFilter < Filter
register_for :token_kind_filter
DEFAULT_OPTIONS = {
:exclude => [],
:include => :all
}
protected
def setup options
super
@group_excluded = false
@exclude = options[:exclude]
@exclude = Array(@exclude) unless @exclude == :all
@include = options[:include]
@include = Array(@include) unless @include == :all
end
def include_text_token? text, kind
include_group? kind
end
def include_group? kind
(@include == :all || @include.include?(kind)) &&
!(@exclude == :all || @exclude.include?(kind))
end
public
# Add the token to the output stream if +kind+ matches the conditions.
def text_token text, kind
super if !@group_excluded && include_text_token?(text, kind)
end
# Add the token group to the output stream if +kind+ matches the
# conditions.
#
# If it does not, all tokens inside the group are excluded from the
# stream, even if their kinds match.
def begin_group kind
if @group_excluded
@group_excluded += 1
elsif include_group? kind
super
else
@group_excluded = 1
end
end
# See +begin_group+.
def begin_line kind
if @group_excluded
@group_excluded += 1
elsif include_group? kind
super
else
@group_excluded = 1
end
end
# Take care of re-enabling the delegation of tokens to the output stream
# if an exluded group has ended.
def end_group kind
if @group_excluded
@group_excluded -= 1
@group_excluded = false if @group_excluded.zero?
else
super
end
end
# See +end_group+.
def end_line kind
if @group_excluded
@group_excluded -= 1
@group_excluded = false if @group_excluded.zero?
else
super
end
end
end
end
end

View File

@ -6,12 +6,11 @@ module Encoders
# Uses REXML. Very slow.
class XML < Encoder
include Streamable
register_for :xml
FILE_EXTENSION = 'xml'
require 'rexml/document'
autoload :REXML, 'rexml/document'
DEFAULT_OPTIONS = {
:tab_width => 8,
@ -20,8 +19,9 @@ module Encoders
}
protected
def setup options
super
@doc = REXML::Document.new
@doc << REXML::XMLDecl.new
@tab_width = options[:tab_width]
@ -29,11 +29,12 @@ module Encoders
end
def finish options
@out = ''
@doc.write @out, options[:pretty], options[:transitive], true
@out
super
end
public
def text_token text, kind
if kind == :space
token = @node
@ -54,11 +55,11 @@ module Encoders
end
end
def open_token kind
def begin_group kind
@node = @node.add_element kind.to_s
end
def close_token kind
def end_group kind
if @node == @root
raise 'no token to close!'
end

View File

@ -0,0 +1,50 @@
autoload :YAML, 'yaml'
module CodeRay
module Encoders
# = YAML Encoder
#
# Slow.
class YAML < Encoder
register_for :yaml
FILE_EXTENSION = 'yaml'
protected
def setup options
super
@data = []
end
def finish options
output ::YAML.dump(@data)
end
public
def text_token text, kind
@data << [text, kind]
end
def begin_group kind
@data << [:begin_group, kind]
end
def end_group kind
@data << [:end_group, kind]
end
def begin_line kind
@data << [:begin_line, kind]
end
def end_line kind
@data << [:end_line, kind]
end
end
end
end

View File

@ -30,7 +30,7 @@ module CodeRay
end
RedCloth::TextileDoc.send :include, ForRedCloth::TextileDoc
RedCloth::Formatters::HTML.module_eval do
def unescape(html)
def unescape(html) # :nodoc:
replacements = {
'&amp;' => '&',
'&quot;' => '"',
@ -45,7 +45,7 @@ module CodeRay
if !opts[:lang] && RedCloth::VERSION.to_s >= '4.2.0'
# simulating pre-4.2 behavior
if opts[:text].sub!(/\A\[(\w+)\]/, '')
if CodeRay::Scanners[$1].plugin_id == 'plaintext'
if CodeRay::Scanners[$1].lang == :text
opts[:text] = $& + opts[:text]
else
opts[:lang] = $1
@ -57,7 +57,7 @@ module CodeRay
@in_bc ||= nil
format = @in_bc ? :div : :span
opts[:text] = unescape(opts[:text]) unless @in_bc
highlighted_code = CodeRay.encode opts[:text], opts[:lang], format, :stream => true
highlighted_code = CodeRay.encode opts[:text], opts[:lang], format
highlighted_code.sub!(/\A<(span|div)/) { |m| m + pba(@in_bc || opts) }
highlighted_code
else
@ -74,7 +74,7 @@ module CodeRay
@in_bc = nil
opts[:lang] ? '' : "</pre>\n"
end
def escape_pre(text)
def escape_pre(text) # :nodoc:
if @in_bc ||= nil
text
else

View File

@ -0,0 +1,141 @@
module CodeRay
# = FileType
#
# A simple filetype recognizer.
#
# == Usage
#
# # determine the type of the given
# lang = FileType[file_name]
#
# # return :text if the file type is unknown
# lang = FileType.fetch file_name, :text
#
# # try the shebang line, too
# lang = FileType.fetch file_name, :text, true
module FileType
UnknownFileType = Class.new Exception
class << self
# Try to determine the file type of the file.
#
# +filename+ is a relative or absolute path to a file.
#
# The file itself is only accessed when +read_shebang+ is set to true.
# That means you can get filetypes from files that don't exist.
def [] filename, read_shebang = false
name = File.basename filename
ext = File.extname(name).sub(/^\./, '') # from last dot, delete the leading dot
ext2 = filename.to_s[/\.(.*)/, 1] # from first dot
type =
TypeFromExt[ext] ||
TypeFromExt[ext.downcase] ||
(TypeFromExt[ext2] if ext2) ||
(TypeFromExt[ext2.downcase] if ext2) ||
TypeFromName[name] ||
TypeFromName[name.downcase]
type ||= shebang(filename) if read_shebang
type
end
# This works like Hash#fetch.
#
# If the filetype cannot be found, the +default+ value
# is returned.
def fetch filename, default = nil, read_shebang = false
if default && block_given?
warn 'Block supersedes default value argument; use either.'
end
if type = self[filename, read_shebang]
type
else
return yield if block_given?
return default if default
raise UnknownFileType, 'Could not determine type of %p.' % filename
end
end
protected
def shebang filename
return unless File.exist? filename
File.open filename, 'r' do |f|
if first_line = f.gets
if type = first_line[TypeFromShebang]
type.to_sym
end
end
end
end
end
TypeFromExt = {
'c' => :c,
'cfc' => :xml,
'cfm' => :xml,
'clj' => :clojure,
'css' => :css,
'diff' => :diff,
'dpr' => :delphi,
'gemspec' => :ruby,
'groovy' => :groovy,
'gvy' => :groovy,
'h' => :c,
'haml' => :haml,
'htm' => :page,
'html' => :page,
'html.erb' => :erb,
'java' => :java,
'js' => :java_script,
'json' => :json,
'mab' => :ruby,
'pas' => :delphi,
'patch' => :diff,
'php' => :php,
'php3' => :php,
'php4' => :php,
'php5' => :php,
'prawn' => :ruby,
'py' => :python,
'py3' => :python,
'pyw' => :python,
'rake' => :ruby,
'raydebug' => :raydebug,
'rb' => :ruby,
'rbw' => :ruby,
'rhtml' => :erb,
'rjs' => :ruby,
'rpdf' => :ruby,
'ru' => :ruby,
'rxml' => :ruby,
# 'sch' => :scheme,
'sql' => :sql,
# 'ss' => :scheme,
'xhtml' => :page,
'xml' => :xml,
'yaml' => :yaml,
'yml' => :yaml,
}
for cpp_alias in %w[cc cpp cp cxx c++ C hh hpp h++ cu]
TypeFromExt[cpp_alias] = :cpp
end
TypeFromShebang = /\b(?:ruby|perl|python|sh)\b/
TypeFromName = {
'Capfile' => :ruby,
'Rakefile' => :ruby,
'Rantfile' => :ruby,
'Gemfile' => :ruby,
}
end
end

View File

@ -0,0 +1,41 @@
module CodeRay
# A simplified interface to the gzip library +zlib+ (from the Ruby Standard Library.)
module GZip
require 'zlib'
# The default zipping level. 7 zips good and fast.
DEFAULT_GZIP_LEVEL = 7
# Unzips the given string +s+.
#
# Example:
# require 'gzip_simple'
# print GZip.gunzip(File.read('adresses.gz'))
def GZip.gunzip s
Zlib::Inflate.inflate s
end
# Zips the given string +s+.
#
# Example:
# require 'gzip_simple'
# File.open('adresses.gz', 'w') do |file
# file.write GZip.gzip('Mum: 0123 456 789', 9)
# end
#
# If you provide a +level+, you can control how strong
# the string is compressed:
# - 0: no compression, only convert to gzip format
# - 1: compress fast
# - 7: compress more, but still fast (default)
# - 8: compress more, slower
# - 9: compress best, very slow
def GZip.gzip s, level = DEFAULT_GZIP_LEVEL
Zlib::Deflate.new(level).deflate s, Zlib::FINISH
end
end
end

View File

@ -0,0 +1,284 @@
module CodeRay
# = PluginHost
#
# A simple subclass/subfolder plugin system.
#
# Example:
# class Generators
# extend PluginHost
# plugin_path 'app/generators'
# end
#
# class Generator
# extend Plugin
# PLUGIN_HOST = Generators
# end
#
# class FancyGenerator < Generator
# register_for :fancy
# end
#
# Generators[:fancy] #-> FancyGenerator
# # or
# CodeRay.require_plugin 'Generators/fancy'
# # or
# Generators::Fancy
module PluginHost
# Raised if Encoders::[] fails because:
# * a file could not be found
# * the requested Plugin is not registered
PluginNotFound = Class.new LoadError
HostNotFound = Class.new LoadError
PLUGIN_HOSTS = []
PLUGIN_HOSTS_BY_ID = {} # dummy hash
# Loads all plugins using list and load.
def load_all
for plugin in list
load plugin
end
end
# Returns the Plugin for +id+.
#
# Example:
# yaml_plugin = MyPluginHost[:yaml]
def [] id, *args, &blk
plugin = validate_id(id)
begin
plugin = plugin_hash.[] plugin, *args, &blk
end while plugin.is_a? Symbol
plugin
end
alias load []
# Tries to +load+ the missing plugin by translating +const+ to the
# underscore form (eg. LinesOfCode becomes lines_of_code).
def const_missing const
id = const.to_s.
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
downcase
load id
end
class << self
# Adds the module/class to the PLUGIN_HOSTS list.
def extended mod
PLUGIN_HOSTS << mod
end
end
# The path where the plugins can be found.
def plugin_path *args
unless args.empty?
@plugin_path = File.expand_path File.join(*args)
end
@plugin_path ||= ''
end
# Map a plugin_id to another.
#
# Usage: Put this in a file plugin_path/_map.rb.
#
# class MyColorHost < PluginHost
# map :navy => :dark_blue,
# :maroon => :brown,
# :luna => :moon
# end
def map hash
for from, to in hash
from = validate_id from
to = validate_id to
plugin_hash[from] = to unless plugin_hash.has_key? from
end
end
# Define the default plugin to use when no plugin is found
# for a given id, or return the default plugin.
#
# See also map.
#
# class MyColorHost < PluginHost
# map :navy => :dark_blue
# default :gray
# end
#
# MyColorHost.default # loads and returns the Gray plugin
def default id = nil
if id
id = validate_id id
raise "The default plugin can't be named \"default\"." if id == :default
plugin_hash[:default] = id
else
load :default
end
end
# Every plugin must register itself for +id+ by calling register_for,
# which calls this method.
#
# See Plugin#register_for.
def register plugin, id
plugin_hash[validate_id(id)] = plugin
end
# A Hash of plugion_id => Plugin pairs.
def plugin_hash
@plugin_hash ||= make_plugin_hash
end
# Returns an array of all .rb files in the plugin path.
#
# The extension .rb is not included.
def list
Dir[path_to('*')].select do |file|
File.basename(file)[/^(?!_)\w+\.rb$/]
end.map do |file|
File.basename(file, '.rb').to_sym
end
end
# Returns an array of all Plugins.
#
# Note: This loads all plugins using load_all.
def all_plugins
load_all
plugin_hash.values.grep(Class)
end
# Loads the map file (see map).
#
# This is done automatically when plugin_path is called.
def load_plugin_map
mapfile = path_to '_map'
@plugin_map_loaded = true
if File.exist? mapfile
require mapfile
true
else
false
end
end
protected
# Return a plugin hash that automatically loads plugins.
def make_plugin_hash
@plugin_map_loaded ||= false
Hash.new do |h, plugin_id|
id = validate_id(plugin_id)
path = path_to id
begin
raise LoadError, "#{path} not found" unless File.exist? path
require path
rescue LoadError => boom
if @plugin_map_loaded
if h.has_key?(:default)
warn '%p could not load plugin %p; falling back to %p' % [self, id, h[:default]]
h[:default]
else
raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
end
else
load_plugin_map
h[plugin_id]
end
else
# Plugin should have registered by now
if h.has_key? id
h[id]
else
raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}."
end
end
end
end
# Returns the expected path to the plugin file for the given id.
def path_to plugin_id
File.join plugin_path, "#{plugin_id}.rb"
end
# Converts +id+ to a Symbol if it is a String,
# or returns +id+ if it already is a Symbol.
#
# Raises +ArgumentError+ for all other objects, or if the
# given String includes non-alphanumeric characters (\W).
def validate_id id
if id.is_a? Symbol or id.nil?
id
elsif id.is_a? String
if id[/\w+/] == id
id.downcase.to_sym
else
raise ArgumentError, "Invalid id given: #{id}"
end
else
raise ArgumentError, "String or Symbol expected, but #{id.class} given."
end
end
end
# = Plugin
#
# Plugins have to include this module.
#
# IMPORTANT: Use extend for this module.
#
# See CodeRay::PluginHost for examples.
module Plugin
attr_reader :plugin_id
# Register this class for the given +id+.
#
# Example:
# class MyPlugin < PluginHost::BaseClass
# register_for :my_id
# ...
# end
#
# See PluginHost.register.
def register_for id
@plugin_id = id
plugin_host.register self, id
end
# Returns the title of the plugin, or sets it to the
# optional argument +title+.
def title title = nil
if title
@title = title.to_s
else
@title ||= name[/([^:]+)$/, 1]
end
end
# The PluginHost for this Plugin class.
def plugin_host host = nil
if host.is_a? PluginHost
const_set :PLUGIN_HOST, host
end
self::PLUGIN_HOST
end
def aliases
plugin_host.load_plugin_map
plugin_host.plugin_hash.inject [] do |aliases, (key, _)|
aliases << key if plugin_host[key] == self
aliases
end
end
end
end

View File

@ -0,0 +1,77 @@
module CodeRay
# = WordList
#
# <b>A Hash subclass designed for mapping word lists to token types.</b>
#
# Copyright (c) 2006-2011 by murphy (Kornelius Kalnbach) <murphy rubychan de>
#
# License:: LGPL / ask the author
# Version:: 2.0 (2011-05-08)
#
# A WordList is a Hash with some additional features.
# It is intended to be used for keyword recognition.
#
# WordList is optimized to be used in Scanners,
# typically to decide whether a given ident is a special token.
#
# For case insensitive words use WordList::CaseIgnoring.
#
# Example:
#
# # define word arrays
# RESERVED_WORDS = %w[
# asm break case continue default do else
# ]
#
# PREDEFINED_TYPES = %w[
# int long short char void
# ]
#
# # make a WordList
# IDENT_KIND = WordList.new(:ident).
# add(RESERVED_WORDS, :reserved).
# add(PREDEFINED_TYPES, :predefined_type)
#
# ...
#
# def scan_tokens tokens, options
# ...
#
# elsif scan(/[A-Za-z_][A-Za-z_0-9]*/)
# # use it
# kind = IDENT_KIND[match]
# ...
class WordList < Hash
# Create a new WordList with +default+ as default value.
def initialize default = false
super default
end
# Add words to the list and associate them with +value+.
#
# Returns +self+, so you can concat add calls.
def add words, value = true
words.each { |word| self[word] = value }
self
end
end
# A CaseIgnoring WordList is like a WordList, only that
# keys are compared case-insensitively (normalizing keys using +downcase+).
class WordList::CaseIgnoring < WordList
def [] key
super key.downcase
end
def []= key, value
super key.downcase, value
end
end
end

Some files were not shown because too many files have changed in this diff Show More