Chris@909
|
1 module CodeRay
|
Chris@909
|
2 module Scanners
|
Chris@909
|
3
|
Chris@909
|
4 # Scanner for output of the diff command.
|
Chris@909
|
5 #
|
Chris@909
|
6 # Alias: +patch+
|
Chris@909
|
7 class Diff < Scanner
|
Chris@909
|
8
|
Chris@909
|
9 register_for :diff
|
Chris@909
|
10 title 'diff output'
|
Chris@909
|
11
|
Chris@909
|
12 DEFAULT_OPTIONS = {
|
Chris@909
|
13 :highlight_code => true,
|
Chris@909
|
14 :inline_diff => true,
|
Chris@909
|
15 }
|
Chris@909
|
16
|
Chris@909
|
17 protected
|
Chris@909
|
18
|
Chris@909
|
19 require 'coderay/helpers/file_type'
|
Chris@909
|
20
|
Chris@909
|
21 def scan_tokens encoder, options
|
Chris@909
|
22
|
Chris@909
|
23 line_kind = nil
|
Chris@909
|
24 state = :initial
|
Chris@909
|
25 deleted_lines = 0
|
Chris@909
|
26 scanners = Hash.new do |h, lang|
|
Chris@909
|
27 h[lang] = Scanners[lang].new '', :keep_tokens => true, :keep_state => true
|
Chris@909
|
28 end
|
Chris@909
|
29 content_scanner = scanners[:plain]
|
Chris@909
|
30 content_scanner_entry_state = nil
|
Chris@909
|
31
|
Chris@909
|
32 until eos?
|
Chris@909
|
33
|
Chris@909
|
34 if match = scan(/\n/)
|
Chris@909
|
35 deleted_lines = 0 unless line_kind == :delete
|
Chris@909
|
36 if line_kind
|
Chris@909
|
37 encoder.end_line line_kind
|
Chris@909
|
38 line_kind = nil
|
Chris@909
|
39 end
|
Chris@909
|
40 encoder.text_token match, :space
|
Chris@909
|
41 next
|
Chris@909
|
42 end
|
Chris@909
|
43
|
Chris@909
|
44 case state
|
Chris@909
|
45
|
Chris@909
|
46 when :initial
|
Chris@909
|
47 if match = scan(/--- |\+\+\+ |=+|_+/)
|
Chris@909
|
48 encoder.begin_line line_kind = :head
|
Chris@909
|
49 encoder.text_token match, :head
|
Chris@909
|
50 if match = scan(/.*?(?=$|[\t\n\x00]| \(revision)/)
|
Chris@909
|
51 encoder.text_token match, :filename
|
Chris@909
|
52 if options[:highlight_code]
|
Chris@909
|
53 file_type = FileType.fetch(match, :text)
|
Chris@909
|
54 file_type = :text if file_type == :diff
|
Chris@909
|
55 content_scanner = scanners[file_type]
|
Chris@909
|
56 content_scanner_entry_state = nil
|
Chris@909
|
57 end
|
Chris@909
|
58 end
|
Chris@909
|
59 next unless match = scan(/.+/)
|
Chris@909
|
60 encoder.text_token match, :plain
|
Chris@909
|
61 elsif match = scan(/Index: |Property changes on: /)
|
Chris@909
|
62 encoder.begin_line line_kind = :head
|
Chris@909
|
63 encoder.text_token match, :head
|
Chris@909
|
64 next unless match = scan(/.+/)
|
Chris@909
|
65 encoder.text_token match, :plain
|
Chris@909
|
66 elsif match = scan(/Added: /)
|
Chris@909
|
67 encoder.begin_line line_kind = :head
|
Chris@909
|
68 encoder.text_token match, :head
|
Chris@909
|
69 next unless match = scan(/.+/)
|
Chris@909
|
70 encoder.text_token match, :plain
|
Chris@909
|
71 state = :added
|
Chris@909
|
72 elsif match = scan(/\\ .*/)
|
Chris@909
|
73 encoder.text_token match, :comment
|
Chris@909
|
74 elsif match = scan(/@@(?>[^@\n]*)@@/)
|
Chris@909
|
75 content_scanner.state = :initial unless match?(/\n\+/)
|
Chris@909
|
76 content_scanner_entry_state = nil
|
Chris@909
|
77 if check(/\n|$/)
|
Chris@909
|
78 encoder.begin_line line_kind = :change
|
Chris@909
|
79 else
|
Chris@909
|
80 encoder.begin_group :change
|
Chris@909
|
81 end
|
Chris@909
|
82 encoder.text_token match[0,2], :change
|
Chris@909
|
83 encoder.text_token match[2...-2], :plain
|
Chris@909
|
84 encoder.text_token match[-2,2], :change
|
Chris@909
|
85 encoder.end_group :change unless line_kind
|
Chris@909
|
86 next unless match = scan(/.+/)
|
Chris@909
|
87 if options[:highlight_code]
|
Chris@909
|
88 content_scanner.tokenize match, :tokens => encoder
|
Chris@909
|
89 else
|
Chris@909
|
90 encoder.text_token match, :plain
|
Chris@909
|
91 end
|
Chris@909
|
92 next
|
Chris@909
|
93 elsif match = scan(/\+/)
|
Chris@909
|
94 encoder.begin_line line_kind = :insert
|
Chris@909
|
95 encoder.text_token match, :insert
|
Chris@909
|
96 next unless match = scan(/.+/)
|
Chris@909
|
97 if options[:highlight_code]
|
Chris@909
|
98 content_scanner.tokenize match, :tokens => encoder
|
Chris@909
|
99 else
|
Chris@909
|
100 encoder.text_token match, :plain
|
Chris@909
|
101 end
|
Chris@909
|
102 next
|
Chris@909
|
103 elsif match = scan(/-/)
|
Chris@909
|
104 deleted_lines += 1
|
Chris@909
|
105 encoder.begin_line line_kind = :delete
|
Chris@909
|
106 encoder.text_token match, :delete
|
Chris@909
|
107 if options[:inline_diff] && deleted_lines == 1 && check(/(?>.*)\n\+(?>.*)$(?!\n\+)/)
|
Chris@909
|
108 content_scanner_entry_state = content_scanner.state
|
Chris@909
|
109 skip(/(.*)\n\+(.*)$/)
|
Chris@909
|
110 head, deletion, insertion, tail = diff self[1], self[2]
|
Chris@909
|
111 pre, deleted, post = content_scanner.tokenize [head, deletion, tail], :tokens => Tokens.new
|
Chris@909
|
112 encoder.tokens pre
|
Chris@909
|
113 unless deleted.empty?
|
Chris@909
|
114 encoder.begin_group :eyecatcher
|
Chris@909
|
115 encoder.tokens deleted
|
Chris@909
|
116 encoder.end_group :eyecatcher
|
Chris@909
|
117 end
|
Chris@909
|
118 encoder.tokens post
|
Chris@909
|
119 encoder.end_line line_kind
|
Chris@909
|
120 encoder.text_token "\n", :space
|
Chris@909
|
121 encoder.begin_line line_kind = :insert
|
Chris@909
|
122 encoder.text_token '+', :insert
|
Chris@909
|
123 content_scanner.state = content_scanner_entry_state || :initial
|
Chris@909
|
124 pre, inserted, post = content_scanner.tokenize [head, insertion, tail], :tokens => Tokens.new
|
Chris@909
|
125 encoder.tokens pre
|
Chris@909
|
126 unless inserted.empty?
|
Chris@909
|
127 encoder.begin_group :eyecatcher
|
Chris@909
|
128 encoder.tokens inserted
|
Chris@909
|
129 encoder.end_group :eyecatcher
|
Chris@909
|
130 end
|
Chris@909
|
131 encoder.tokens post
|
Chris@909
|
132 elsif match = scan(/.*/)
|
Chris@909
|
133 if options[:highlight_code]
|
Chris@909
|
134 if deleted_lines == 1
|
Chris@909
|
135 content_scanner_entry_state = content_scanner.state
|
Chris@909
|
136 end
|
Chris@909
|
137 content_scanner.tokenize match, :tokens => encoder unless match.empty?
|
Chris@909
|
138 if !match?(/\n-/)
|
Chris@909
|
139 if match?(/\n\+/)
|
Chris@909
|
140 content_scanner.state = content_scanner_entry_state || :initial
|
Chris@909
|
141 end
|
Chris@909
|
142 content_scanner_entry_state = nil
|
Chris@909
|
143 end
|
Chris@909
|
144 else
|
Chris@909
|
145 encoder.text_token match, :plain
|
Chris@909
|
146 end
|
Chris@909
|
147 end
|
Chris@909
|
148 next
|
Chris@909
|
149 elsif match = scan(/ .*/)
|
Chris@909
|
150 if options[:highlight_code]
|
Chris@909
|
151 content_scanner.tokenize match, :tokens => encoder
|
Chris@909
|
152 else
|
Chris@909
|
153 encoder.text_token match, :plain
|
Chris@909
|
154 end
|
Chris@909
|
155 next
|
Chris@909
|
156 elsif match = scan(/.+/)
|
Chris@909
|
157 encoder.begin_line line_kind = :comment
|
Chris@909
|
158 encoder.text_token match, :plain
|
Chris@909
|
159 else
|
Chris@909
|
160 raise_inspect 'else case rached'
|
Chris@909
|
161 end
|
Chris@909
|
162
|
Chris@909
|
163 when :added
|
Chris@909
|
164 if match = scan(/ \+/)
|
Chris@909
|
165 encoder.begin_line line_kind = :insert
|
Chris@909
|
166 encoder.text_token match, :insert
|
Chris@909
|
167 next unless match = scan(/.+/)
|
Chris@909
|
168 encoder.text_token match, :plain
|
Chris@909
|
169 else
|
Chris@909
|
170 state = :initial
|
Chris@909
|
171 next
|
Chris@909
|
172 end
|
Chris@909
|
173 end
|
Chris@909
|
174
|
Chris@909
|
175 end
|
Chris@909
|
176
|
Chris@909
|
177 encoder.end_line line_kind if line_kind
|
Chris@909
|
178
|
Chris@909
|
179 encoder
|
Chris@909
|
180 end
|
Chris@909
|
181
|
Chris@909
|
182 private
|
Chris@909
|
183
|
Chris@909
|
184 def diff a, b
|
Chris@909
|
185 # i will be the index of the leftmost difference from the left.
|
Chris@909
|
186 i_max = [a.size, b.size].min
|
Chris@909
|
187 i = 0
|
Chris@909
|
188 i += 1 while i < i_max && a[i] == b[i]
|
Chris@909
|
189 # j_min will be the index of the leftmost difference from the right.
|
Chris@909
|
190 j_min = i - i_max
|
Chris@909
|
191 # j will be the index of the rightmost difference from the right which
|
Chris@909
|
192 # does not precede the leftmost one from the left.
|
Chris@909
|
193 j = -1
|
Chris@909
|
194 j -= 1 while j >= j_min && a[j] == b[j]
|
Chris@909
|
195 return a[0...i], a[i..j], b[i..j], (j < -1) ? a[j+1..-1] : ''
|
Chris@909
|
196 end
|
Chris@909
|
197
|
Chris@909
|
198 end
|
Chris@909
|
199
|
Chris@909
|
200 end
|
Chris@909
|
201 end
|