coderay 0.9.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/{lib/README → README_INDEX.rdoc} +10 -21
  2. data/Rakefile +6 -6
  3. data/bin/coderay +193 -64
  4. data/lib/coderay.rb +61 -105
  5. data/lib/coderay/duo.rb +17 -21
  6. data/lib/coderay/encoder.rb +100 -112
  7. data/lib/coderay/encoders/_map.rb +12 -7
  8. data/lib/coderay/encoders/comment_filter.rb +12 -30
  9. data/lib/coderay/encoders/count.rb +29 -11
  10. data/lib/coderay/encoders/debug.rb +32 -20
  11. data/lib/coderay/encoders/div.rb +13 -9
  12. data/lib/coderay/encoders/filter.rb +34 -51
  13. data/lib/coderay/encoders/html.rb +155 -161
  14. data/lib/coderay/encoders/html/css.rb +4 -9
  15. data/lib/coderay/encoders/html/numbering.rb +115 -0
  16. data/lib/coderay/encoders/html/output.rb +22 -70
  17. data/lib/coderay/encoders/json.rb +59 -45
  18. data/lib/coderay/encoders/lines_of_code.rb +12 -57
  19. data/lib/coderay/encoders/null.rb +6 -14
  20. data/lib/coderay/encoders/page.rb +13 -9
  21. data/lib/coderay/encoders/span.rb +13 -9
  22. data/lib/coderay/encoders/statistic.rb +58 -39
  23. data/lib/coderay/encoders/terminal.rb +179 -0
  24. data/lib/coderay/encoders/text.rb +31 -17
  25. data/lib/coderay/encoders/token_kind_filter.rb +111 -0
  26. data/lib/coderay/encoders/xml.rb +19 -18
  27. data/lib/coderay/encoders/yaml.rb +37 -9
  28. data/lib/coderay/for_redcloth.rb +4 -4
  29. data/lib/coderay/helpers/file_type.rb +127 -246
  30. data/lib/coderay/helpers/gzip.rb +41 -0
  31. data/lib/coderay/helpers/plugin.rb +241 -306
  32. data/lib/coderay/helpers/word_list.rb +65 -126
  33. data/lib/coderay/scanner.rb +173 -156
  34. data/lib/coderay/scanners/_map.rb +18 -17
  35. data/lib/coderay/scanners/c.rb +63 -77
  36. data/lib/coderay/scanners/clojure.rb +217 -0
  37. data/lib/coderay/scanners/cpp.rb +71 -84
  38. data/lib/coderay/scanners/css.rb +103 -120
  39. data/lib/coderay/scanners/debug.rb +47 -44
  40. data/lib/coderay/scanners/delphi.rb +70 -76
  41. data/lib/coderay/scanners/diff.rb +141 -50
  42. data/lib/coderay/scanners/erb.rb +81 -0
  43. data/lib/coderay/scanners/groovy.rb +104 -113
  44. data/lib/coderay/scanners/haml.rb +168 -0
  45. data/lib/coderay/scanners/html.rb +181 -110
  46. data/lib/coderay/scanners/java.rb +73 -75
  47. data/lib/coderay/scanners/java/builtin_types.rb +2 -0
  48. data/lib/coderay/scanners/java_script.rb +90 -101
  49. data/lib/coderay/scanners/json.rb +40 -53
  50. data/lib/coderay/scanners/php.rb +123 -147
  51. data/lib/coderay/scanners/python.rb +93 -91
  52. data/lib/coderay/scanners/raydebug.rb +66 -0
  53. data/lib/coderay/scanners/ruby.rb +343 -326
  54. data/lib/coderay/scanners/ruby/patterns.rb +40 -106
  55. data/lib/coderay/scanners/ruby/string_state.rb +71 -0
  56. data/lib/coderay/scanners/sql.rb +80 -66
  57. data/lib/coderay/scanners/text.rb +26 -0
  58. data/lib/coderay/scanners/xml.rb +1 -1
  59. data/lib/coderay/scanners/yaml.rb +74 -73
  60. data/lib/coderay/style.rb +10 -7
  61. data/lib/coderay/styles/_map.rb +3 -3
  62. data/lib/coderay/styles/alpha.rb +143 -0
  63. data/lib/coderay/token_kinds.rb +90 -0
  64. data/lib/coderay/tokens.rb +102 -277
  65. data/lib/coderay/tokens_proxy.rb +55 -0
  66. data/lib/coderay/version.rb +3 -0
  67. data/test/functional/basic.rb +200 -18
  68. data/test/functional/examples.rb +130 -0
  69. data/test/functional/for_redcloth.rb +15 -8
  70. data/test/functional/suite.rb +9 -6
  71. metadata +103 -123
  72. data/FOLDERS +0 -53
  73. data/bin/coderay_stylesheet +0 -4
  74. data/lib/coderay/encoders/html/numerization.rb +0 -133
  75. data/lib/coderay/encoders/term.rb +0 -158
  76. data/lib/coderay/encoders/token_class_filter.rb +0 -84
  77. data/lib/coderay/helpers/gzip_simple.rb +0 -123
  78. data/lib/coderay/scanners/nitro_xhtml.rb +0 -136
  79. data/lib/coderay/scanners/plaintext.rb +0 -20
  80. data/lib/coderay/scanners/rhtml.rb +0 -78
  81. data/lib/coderay/scanners/scheme.rb +0 -145
  82. data/lib/coderay/styles/cycnus.rb +0 -152
  83. data/lib/coderay/styles/murphy.rb +0 -134
  84. data/lib/coderay/token_classes.rb +0 -86
  85. data/test/functional/load_plugin_scanner.rb +0 -11
  86. data/test/functional/vhdl.rb +0 -126
  87. data/test/functional/word_list.rb +0 -79
@@ -1,62 +1,65 @@
1
1
  module CodeRay
2
2
  module Scanners
3
-
3
+
4
4
  # = Debug Scanner
5
+ #
6
+ # Interprets the output of the Encoders::Debug encoder.
5
7
  class Debug < Scanner
6
-
7
- include Streamable
8
+
8
9
  register_for :debug
9
- file_extension 'raydebug'
10
- title 'CodeRay Token Dump'
11
-
10
+ title 'CodeRay Token Dump Import'
11
+
12
12
  protected
13
- def scan_tokens tokens, options
14
-
13
+
14
+ def scan_tokens encoder, options
15
+
15
16
  opened_tokens = []
16
-
17
+
17
18
  until eos?
18
-
19
- kind = nil
20
- match = nil
21
-
22
- if scan(/\s+/)
23
- tokens << [matched, :space]
24
- next
25
-
26
- elsif scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \) /x)
27
- kind = self[1].to_sym
28
- match = self[2].gsub(/\\(.)/, '\1')
29
-
30
- elsif scan(/ (\w+) < /x)
31
- kind = self[1].to_sym
32
- opened_tokens << kind
33
- match = :open
34
-
35
- elsif !opened_tokens.empty? && scan(/ > /x)
36
- kind = opened_tokens.pop || :error
37
- match = :close
38
-
39
- else
19
+
20
+ if match = scan(/\s+/)
21
+ encoder.text_token match, :space
22
+
23
+ elsif match = scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \)? /x)
24
+ kind = self[1].to_sym
25
+ match = self[2].gsub(/\\(.)/m, '\1')
26
+ unless TokenKinds.has_key? kind
40
27
  kind = :error
41
- getch
42
-
28
+ match = matched
43
29
  end
44
-
45
- match ||= matched
46
- if $CODERAY_DEBUG and not kind
47
- raise_inspect 'Error token %p in line %d' %
48
- [[match, kind], line], tokens
30
+ encoder.text_token match, kind
31
+
32
+ elsif match = scan(/ (\w+) ([<\[]) /x)
33
+ kind = self[1].to_sym
34
+ opened_tokens << kind
35
+ case self[2]
36
+ when '<'
37
+ encoder.begin_group kind
38
+ when '['
39
+ encoder.begin_line kind
40
+ else
41
+ raise 'CodeRay bug: This case should not be reached.'
42
+ end
43
+
44
+ elsif !opened_tokens.empty? && match = scan(/ > /x)
45
+ encoder.end_group opened_tokens.pop
46
+
47
+ elsif !opened_tokens.empty? && match = scan(/ \] /x)
48
+ encoder.end_line opened_tokens.pop
49
+
50
+ else
51
+ encoder.text_token getch, :space
52
+
49
53
  end
50
- raise_inspect 'Empty token', tokens unless match
51
-
52
- tokens << [match, kind]
53
54
 
54
55
  end
55
56
 
56
- tokens
57
+ encoder.end_group opened_tokens.pop until opened_tokens.empty?
58
+
59
+ encoder
57
60
  end
58
-
61
+
59
62
  end
60
-
63
+
61
64
  end
62
65
  end
@@ -1,12 +1,15 @@
1
1
  module CodeRay
2
2
  module Scanners
3
3
 
4
+ # Scanner for the Delphi language (Object Pascal).
5
+ #
6
+ # Alias: +pascal+
4
7
  class Delphi < Scanner
5
-
8
+
6
9
  register_for :delphi
7
10
  file_extension 'pas'
8
11
 
9
- RESERVED_WORDS = [
12
+ KEYWORDS = [
10
13
  'and', 'array', 'as', 'at', 'asm', 'at', 'begin', 'case', 'class',
11
14
  'const', 'constructor', 'destructor', 'dispinterface', 'div', 'do',
12
15
  'downto', 'else', 'end', 'except', 'exports', 'file', 'finalization',
@@ -16,9 +19,9 @@ module Scanners
16
19
  'procedure', 'program', 'property', 'raise', 'record', 'repeat',
17
20
  'resourcestring', 'set', 'shl', 'shr', 'string', 'then', 'threadvar',
18
21
  'to', 'try', 'type', 'unit', 'until', 'uses', 'var', 'while', 'with',
19
- 'xor', 'on'
20
- ]
21
-
22
+ 'xor', 'on',
23
+ ] # :nodoc:
24
+
22
25
  DIRECTIVES = [
23
26
  'absolute', 'abstract', 'assembler', 'at', 'automated', 'cdecl',
24
27
  'contains', 'deprecated', 'dispid', 'dynamic', 'export',
@@ -27,121 +30,112 @@ module Scanners
27
30
  'package', 'pascal', 'platform', 'private', 'protected', 'public',
28
31
  'published', 'read', 'readonly', 'register', 'reintroduce',
29
32
  'requires', 'resident', 'safecall', 'stdcall', 'stored', 'varargs',
30
- 'virtual', 'write', 'writeonly'
31
- ]
32
-
33
- IDENT_KIND = CaseIgnoringWordList.new(:ident).
34
- add(RESERVED_WORDS, :reserved).
35
- add(DIRECTIVES, :directive)
33
+ 'virtual', 'write', 'writeonly',
34
+ ] # :nodoc:
36
35
 
37
- NAME_FOLLOWS = CaseIgnoringWordList.new(false).
38
- add(%w(procedure function .))
39
-
40
- private
41
- def scan_tokens tokens, options
42
-
36
+ IDENT_KIND = WordList::CaseIgnoring.new(:ident).
37
+ add(KEYWORDS, :keyword).
38
+ add(DIRECTIVES, :directive) # :nodoc:
39
+
40
+ NAME_FOLLOWS = WordList::CaseIgnoring.new(false).
41
+ add(%w(procedure function .)) # :nodoc:
42
+
43
+ protected
44
+
45
+ def scan_tokens encoder, options
46
+
43
47
  state = :initial
44
48
  last_token = ''
45
-
49
+
46
50
  until eos?
47
-
48
- kind = nil
49
- match = nil
50
-
51
+
51
52
  if state == :initial
52
53
 
53
- if scan(/ \s+ /x)
54
- tokens << [matched, :space]
54
+ if match = scan(/ \s+ /x)
55
+ encoder.text_token match, :space
55
56
  next
56
57
 
57
- elsif scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx)
58
- tokens << [matched, :preprocessor]
58
+ elsif match = scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx)
59
+ encoder.text_token match, :preprocessor
59
60
  next
60
61
 
61
- elsif scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx)
62
- tokens << [matched, :comment]
62
+ elsif match = scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx)
63
+ encoder.text_token match, :comment
63
64
  next
64
65
 
65
66
  elsif match = scan(/ <[>=]? | >=? | :=? | [-+=*\/;,@\^|\(\)\[\]] | \.\. /x)
66
- kind = :operator
67
+ encoder.text_token match, :operator
67
68
 
68
69
  elsif match = scan(/\./)
69
- kind = :operator
70
- if last_token == 'end'
71
- tokens << [match, kind]
72
- next
73
- end
70
+ encoder.text_token match, :operator
71
+ next if last_token == 'end'
74
72
 
75
73
  elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
76
- kind = NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match]
74
+ encoder.text_token match, NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match]
77
75
 
78
- elsif match = scan(/ ' ( [^\n']|'' ) (?:'|$) /x)
79
- tokens << [:open, :char]
80
- tokens << ["'", :delimiter]
81
- tokens << [self[1], :content]
82
- tokens << ["'", :delimiter]
83
- tokens << [:close, :char]
76
+ elsif match = skip(/ ' ( [^\n']|'' ) (?:'|$) /x)
77
+ encoder.begin_group :char
78
+ encoder.text_token "'", :delimiter
79
+ encoder.text_token self[1], :content
80
+ encoder.text_token "'", :delimiter
81
+ encoder.end_group :char
84
82
  next
85
83
 
86
84
  elsif match = scan(/ ' /x)
87
- tokens << [:open, :string]
85
+ encoder.begin_group :string
86
+ encoder.text_token match, :delimiter
88
87
  state = :string
89
- kind = :delimiter
90
88
 
91
- elsif scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x)
92
- kind = :char
89
+ elsif match = scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x)
90
+ encoder.text_token match, :char
93
91
 
94
- elsif scan(/ \$ [0-9A-Fa-f]+ /x)
95
- kind = :hex
92
+ elsif match = scan(/ \$ [0-9A-Fa-f]+ /x)
93
+ encoder.text_token match, :hex
96
94
 
97
- elsif scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x)
98
- kind = :integer
95
+ elsif match = scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x)
96
+ encoder.text_token match, :integer
97
+
98
+ elsif match = scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x)
99
+ encoder.text_token match, :float
99
100
 
100
- elsif scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x)
101
- kind = :float
102
-
103
101
  else
104
- kind = :error
105
- getch
106
-
102
+ encoder.text_token getch, :error
103
+ next
104
+
107
105
  end
108
106
 
109
107
  elsif state == :string
110
- if scan(/[^\n']+/)
111
- kind = :content
112
- elsif scan(/''/)
113
- kind = :char
114
- elsif scan(/'/)
115
- tokens << ["'", :delimiter]
116
- tokens << [:close, :string]
108
+ if match = scan(/[^\n']+/)
109
+ encoder.text_token match, :content
110
+ elsif match = scan(/''/)
111
+ encoder.text_token match, :char
112
+ elsif match = scan(/'/)
113
+ encoder.text_token match, :delimiter
114
+ encoder.end_group :string
117
115
  state = :initial
118
116
  next
119
- elsif scan(/\n/)
120
- tokens << [:close, :string]
121
- kind = :error
117
+ elsif match = scan(/\n/)
118
+ encoder.end_group :string
119
+ encoder.text_token match, :space
122
120
  state = :initial
123
121
  else
124
- raise "else case \' reached; %p not handled." % peek(1), tokens
122
+ raise "else case \' reached; %p not handled." % peek(1), encoder
125
123
  end
126
124
 
127
125
  else
128
- raise 'else-case reached', tokens
126
+ raise 'else-case reached', encoder
129
127
 
130
128
  end
131
129
 
132
- match ||= matched
133
- if $CODERAY_DEBUG and not kind
134
- raise_inspect 'Error token %p in line %d' %
135
- [[match, kind], line], tokens, state
136
- end
137
- raise_inspect 'Empty token', tokens unless match
138
-
139
130
  last_token = match
140
- tokens << [match, kind]
141
131
 
142
132
  end
143
133
 
144
- tokens
134
+ if state == :string
135
+ encoder.end_group state
136
+ end
137
+
138
+ encoder
145
139
  end
146
140
 
147
141
  end
@@ -1,25 +1,43 @@
1
1
  module CodeRay
2
2
  module Scanners
3
3
 
4
+ # Scanner for output of the diff command.
5
+ #
6
+ # Alias: +patch+
4
7
  class Diff < Scanner
5
8
 
6
9
  register_for :diff
7
10
  title 'diff output'
8
11
 
9
- def scan_tokens tokens, options
12
+ DEFAULT_OPTIONS = {
13
+ :highlight_code => true,
14
+ :inline_diff => true,
15
+ }
16
+
17
+ protected
18
+
19
+ require 'coderay/helpers/file_type'
20
+
21
+ def scan_tokens encoder, options
10
22
 
11
23
  line_kind = nil
12
24
  state = :initial
25
+ deleted_lines = 0
26
+ scanners = Hash.new do |h, lang|
27
+ h[lang] = Scanners[lang].new '', :keep_tokens => true, :keep_state => true
28
+ end
29
+ content_scanner = scanners[:plain]
30
+ content_scanner_entry_state = nil
13
31
 
14
32
  until eos?
15
- kind = match = nil
16
33
 
17
34
  if match = scan(/\n/)
35
+ deleted_lines = 0 unless line_kind == :delete
18
36
  if line_kind
19
- tokens << [:end_line, line_kind]
37
+ encoder.end_line line_kind
20
38
  line_kind = nil
21
39
  end
22
- tokens << [match, :space]
40
+ encoder.text_token match, :space
23
41
  next
24
42
  end
25
43
 
@@ -27,81 +45,154 @@ module Scanners
27
45
 
28
46
  when :initial
29
47
  if match = scan(/--- |\+\+\+ |=+|_+/)
30
- tokens << [:begin_line, line_kind = :head]
31
- tokens << [match, :head]
48
+ encoder.begin_line line_kind = :head
49
+ encoder.text_token match, :head
50
+ if match = scan(/.*?(?=$|[\t\n\x00]| \(revision)/)
51
+ encoder.text_token match, :filename
52
+ if options[:highlight_code]
53
+ file_type = FileType.fetch(match, :text)
54
+ file_type = :text if file_type == :diff
55
+ content_scanner = scanners[file_type]
56
+ content_scanner_entry_state = nil
57
+ end
58
+ end
32
59
  next unless match = scan(/.+/)
33
- kind = :plain
60
+ encoder.text_token match, :plain
34
61
  elsif match = scan(/Index: |Property changes on: /)
35
- tokens << [:begin_line, line_kind = :head]
36
- tokens << [match, :head]
62
+ encoder.begin_line line_kind = :head
63
+ encoder.text_token match, :head
37
64
  next unless match = scan(/.+/)
38
- kind = :plain
65
+ encoder.text_token match, :plain
39
66
  elsif match = scan(/Added: /)
40
- tokens << [:begin_line, line_kind = :head]
41
- tokens << [match, :head]
67
+ encoder.begin_line line_kind = :head
68
+ encoder.text_token match, :head
42
69
  next unless match = scan(/.+/)
43
- kind = :plain
70
+ encoder.text_token match, :plain
44
71
  state = :added
45
- elsif match = scan(/\\ /)
46
- tokens << [:begin_line, line_kind = :change]
47
- tokens << [match, :change]
48
- next unless match = scan(/.+/)
49
- kind = :plain
72
+ elsif match = scan(/\\ .*/)
73
+ encoder.text_token match, :comment
50
74
  elsif match = scan(/@@(?>[^@\n]*)@@/)
75
+ content_scanner.state = :initial unless match?(/\n\+/)
76
+ content_scanner_entry_state = nil
51
77
  if check(/\n|$/)
52
- tokens << [:begin_line, line_kind = :change]
78
+ encoder.begin_line line_kind = :change
53
79
  else
54
- tokens << [:open, :change]
80
+ encoder.begin_group :change
55
81
  end
56
- tokens << [match[0,2], :change]
57
- tokens << [match[2...-2], :plain]
58
- tokens << [match[-2,2], :change]
59
- tokens << [:close, :change] unless line_kind
82
+ encoder.text_token match[0,2], :change
83
+ encoder.text_token match[2...-2], :plain
84
+ encoder.text_token match[-2,2], :change
85
+ encoder.end_group :change unless line_kind
60
86
  next unless match = scan(/.+/)
61
- kind = :plain
87
+ if options[:highlight_code]
88
+ content_scanner.tokenize match, :tokens => encoder
89
+ else
90
+ encoder.text_token match, :plain
91
+ end
92
+ next
62
93
  elsif match = scan(/\+/)
63
- tokens << [:begin_line, line_kind = :insert]
64
- tokens << [match, :insert]
94
+ encoder.begin_line line_kind = :insert
95
+ encoder.text_token match, :insert
65
96
  next unless match = scan(/.+/)
66
- kind = :plain
97
+ if options[:highlight_code]
98
+ content_scanner.tokenize match, :tokens => encoder
99
+ else
100
+ encoder.text_token match, :plain
101
+ end
102
+ next
67
103
  elsif match = scan(/-/)
68
- tokens << [:begin_line, line_kind = :delete]
69
- tokens << [match, :delete]
70
- next unless match = scan(/.+/)
71
- kind = :plain
72
- elsif scan(/ .*/)
73
- kind = :comment
74
- elsif scan(/.+/)
75
- tokens << [:begin_line, line_kind = :comment]
76
- kind = :plain
104
+ deleted_lines += 1
105
+ encoder.begin_line line_kind = :delete
106
+ encoder.text_token match, :delete
107
+ if options[:inline_diff] && deleted_lines == 1 && check(/(?>.*)\n\+(?>.*)$(?!\n\+)/)
108
+ content_scanner_entry_state = content_scanner.state
109
+ skip(/(.*)\n\+(.*)$/)
110
+ head, deletion, insertion, tail = diff self[1], self[2]
111
+ pre, deleted, post = content_scanner.tokenize [head, deletion, tail], :tokens => Tokens.new
112
+ encoder.tokens pre
113
+ unless deleted.empty?
114
+ encoder.begin_group :eyecatcher
115
+ encoder.tokens deleted
116
+ encoder.end_group :eyecatcher
117
+ end
118
+ encoder.tokens post
119
+ encoder.end_line line_kind
120
+ encoder.text_token "\n", :space
121
+ encoder.begin_line line_kind = :insert
122
+ encoder.text_token '+', :insert
123
+ content_scanner.state = content_scanner_entry_state || :initial
124
+ pre, inserted, post = content_scanner.tokenize [head, insertion, tail], :tokens => Tokens.new
125
+ encoder.tokens pre
126
+ unless inserted.empty?
127
+ encoder.begin_group :eyecatcher
128
+ encoder.tokens inserted
129
+ encoder.end_group :eyecatcher
130
+ end
131
+ encoder.tokens post
132
+ elsif match = scan(/.*/)
133
+ if options[:highlight_code]
134
+ if deleted_lines == 1
135
+ content_scanner_entry_state = content_scanner.state
136
+ end
137
+ content_scanner.tokenize match, :tokens => encoder unless match.empty?
138
+ if !match?(/\n-/)
139
+ if match?(/\n\+/)
140
+ content_scanner.state = content_scanner_entry_state || :initial
141
+ end
142
+ content_scanner_entry_state = nil
143
+ end
144
+ else
145
+ encoder.text_token match, :plain
146
+ end
147
+ end
148
+ next
149
+ elsif match = scan(/ .*/)
150
+ if options[:highlight_code]
151
+ content_scanner.tokenize match, :tokens => encoder
152
+ else
153
+ encoder.text_token match, :plain
154
+ end
155
+ next
156
+ elsif match = scan(/.+/)
157
+ encoder.begin_line line_kind = :comment
158
+ encoder.text_token match, :plain
77
159
  else
78
160
  raise_inspect 'else case rached'
79
161
  end
80
162
 
81
163
  when :added
82
164
  if match = scan(/ \+/)
83
- tokens << [:begin_line, line_kind = :insert]
84
- tokens << [match, :insert]
165
+ encoder.begin_line line_kind = :insert
166
+ encoder.text_token match, :insert
85
167
  next unless match = scan(/.+/)
86
- kind = :plain
168
+ encoder.text_token match, :plain
87
169
  else
88
170
  state = :initial
89
171
  next
90
172
  end
91
173
  end
92
174
 
93
- match ||= matched
94
- if $CODERAY_DEBUG and not kind
95
- raise_inspect 'Error token %p in line %d' %
96
- [[match, kind], line], tokens
97
- end
98
- raise_inspect 'Empty token', tokens unless match
99
-
100
- tokens << [match, kind]
101
175
  end
102
176
 
103
- tokens << [:end_line, line_kind] if line_kind
104
- tokens
177
+ encoder.end_line line_kind if line_kind
178
+
179
+ encoder
180
+ end
181
+
182
+ private
183
+
184
+ def diff a, b
185
+ # i will be the index of the leftmost difference from the left.
186
+ i_max = [a.size, b.size].min
187
+ i = 0
188
+ i += 1 while i < i_max && a[i] == b[i]
189
+ # j_min will be the index of the leftmost difference from the right.
190
+ j_min = i - i_max
191
+ # j will be the index of the rightmost difference from the right which
192
+ # does not precede the leftmost one from the left.
193
+ j = -1
194
+ j -= 1 while j >= j_min && a[j] == b[j]
195
+ return a[0...i], a[i..j], b[i..j], (j < -1) ? a[j+1..-1] : ''
105
196
  end
106
197
 
107
198
  end