coderay 0.9.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/{lib/README → README_INDEX.rdoc} +10 -21
- data/Rakefile +6 -6
- data/bin/coderay +193 -64
- data/lib/coderay.rb +61 -105
- data/lib/coderay/duo.rb +17 -21
- data/lib/coderay/encoder.rb +100 -112
- data/lib/coderay/encoders/_map.rb +12 -7
- data/lib/coderay/encoders/comment_filter.rb +12 -30
- data/lib/coderay/encoders/count.rb +29 -11
- data/lib/coderay/encoders/debug.rb +32 -20
- data/lib/coderay/encoders/div.rb +13 -9
- data/lib/coderay/encoders/filter.rb +34 -51
- data/lib/coderay/encoders/html.rb +155 -161
- data/lib/coderay/encoders/html/css.rb +4 -9
- data/lib/coderay/encoders/html/numbering.rb +115 -0
- data/lib/coderay/encoders/html/output.rb +22 -70
- data/lib/coderay/encoders/json.rb +59 -45
- data/lib/coderay/encoders/lines_of_code.rb +12 -57
- data/lib/coderay/encoders/null.rb +6 -14
- data/lib/coderay/encoders/page.rb +13 -9
- data/lib/coderay/encoders/span.rb +13 -9
- data/lib/coderay/encoders/statistic.rb +58 -39
- data/lib/coderay/encoders/terminal.rb +179 -0
- data/lib/coderay/encoders/text.rb +31 -17
- data/lib/coderay/encoders/token_kind_filter.rb +111 -0
- data/lib/coderay/encoders/xml.rb +19 -18
- data/lib/coderay/encoders/yaml.rb +37 -9
- data/lib/coderay/for_redcloth.rb +4 -4
- data/lib/coderay/helpers/file_type.rb +127 -246
- data/lib/coderay/helpers/gzip.rb +41 -0
- data/lib/coderay/helpers/plugin.rb +241 -306
- data/lib/coderay/helpers/word_list.rb +65 -126
- data/lib/coderay/scanner.rb +173 -156
- data/lib/coderay/scanners/_map.rb +18 -17
- data/lib/coderay/scanners/c.rb +63 -77
- data/lib/coderay/scanners/clojure.rb +217 -0
- data/lib/coderay/scanners/cpp.rb +71 -84
- data/lib/coderay/scanners/css.rb +103 -120
- data/lib/coderay/scanners/debug.rb +47 -44
- data/lib/coderay/scanners/delphi.rb +70 -76
- data/lib/coderay/scanners/diff.rb +141 -50
- data/lib/coderay/scanners/erb.rb +81 -0
- data/lib/coderay/scanners/groovy.rb +104 -113
- data/lib/coderay/scanners/haml.rb +168 -0
- data/lib/coderay/scanners/html.rb +181 -110
- data/lib/coderay/scanners/java.rb +73 -75
- data/lib/coderay/scanners/java/builtin_types.rb +2 -0
- data/lib/coderay/scanners/java_script.rb +90 -101
- data/lib/coderay/scanners/json.rb +40 -53
- data/lib/coderay/scanners/php.rb +123 -147
- data/lib/coderay/scanners/python.rb +93 -91
- data/lib/coderay/scanners/raydebug.rb +66 -0
- data/lib/coderay/scanners/ruby.rb +343 -326
- data/lib/coderay/scanners/ruby/patterns.rb +40 -106
- data/lib/coderay/scanners/ruby/string_state.rb +71 -0
- data/lib/coderay/scanners/sql.rb +80 -66
- data/lib/coderay/scanners/text.rb +26 -0
- data/lib/coderay/scanners/xml.rb +1 -1
- data/lib/coderay/scanners/yaml.rb +74 -73
- data/lib/coderay/style.rb +10 -7
- data/lib/coderay/styles/_map.rb +3 -3
- data/lib/coderay/styles/alpha.rb +143 -0
- data/lib/coderay/token_kinds.rb +90 -0
- data/lib/coderay/tokens.rb +102 -277
- data/lib/coderay/tokens_proxy.rb +55 -0
- data/lib/coderay/version.rb +3 -0
- data/test/functional/basic.rb +200 -18
- data/test/functional/examples.rb +130 -0
- data/test/functional/for_redcloth.rb +15 -8
- data/test/functional/suite.rb +9 -6
- metadata +103 -123
- data/FOLDERS +0 -53
- data/bin/coderay_stylesheet +0 -4
- data/lib/coderay/encoders/html/numerization.rb +0 -133
- data/lib/coderay/encoders/term.rb +0 -158
- data/lib/coderay/encoders/token_class_filter.rb +0 -84
- data/lib/coderay/helpers/gzip_simple.rb +0 -123
- data/lib/coderay/scanners/nitro_xhtml.rb +0 -136
- data/lib/coderay/scanners/plaintext.rb +0 -20
- data/lib/coderay/scanners/rhtml.rb +0 -78
- data/lib/coderay/scanners/scheme.rb +0 -145
- data/lib/coderay/styles/cycnus.rb +0 -152
- data/lib/coderay/styles/murphy.rb +0 -134
- data/lib/coderay/token_classes.rb +0 -86
- data/test/functional/load_plugin_scanner.rb +0 -11
- data/test/functional/vhdl.rb +0 -126
- data/test/functional/word_list.rb +0 -79
@@ -2,7 +2,7 @@ module CodeRay
|
|
2
2
|
module Encoders
|
3
3
|
|
4
4
|
class HTML
|
5
|
-
class CSS
|
5
|
+
class CSS # :nodoc:
|
6
6
|
|
7
7
|
attr :stylesheet
|
8
8
|
|
@@ -20,11 +20,11 @@ module Encoders
|
|
20
20
|
parse style::TOKEN_COLORS
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
23
|
+
def get_style styles
|
24
24
|
cl = @classes[styles.first]
|
25
25
|
return '' unless cl
|
26
26
|
style = ''
|
27
|
-
1.upto
|
27
|
+
1.upto styles.size do |offset|
|
28
28
|
break if style = cl[styles[offset .. -1]]
|
29
29
|
end
|
30
30
|
# warn 'Style not found: %p' % [styles] if style.empty?
|
@@ -44,7 +44,7 @@ module Encoders
|
|
44
44
|
( [^\}]+ )? # $2 = style
|
45
45
|
\s* \} \s*
|
46
46
|
|
|
47
|
-
(
|
47
|
+
( [^\n]+ ) # $3 = error
|
48
48
|
/mx
|
49
49
|
def parse stylesheet
|
50
50
|
stylesheet.scan CSS_CLASS_PATTERN do |selectors, style, error|
|
@@ -63,8 +63,3 @@ module Encoders
|
|
63
63
|
|
64
64
|
end
|
65
65
|
end
|
66
|
-
|
67
|
-
if $0 == __FILE__
|
68
|
-
require 'pp'
|
69
|
-
pp CodeRay::Encoders::HTML::CSS.new
|
70
|
-
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module CodeRay
|
2
|
+
module Encoders
|
3
|
+
|
4
|
+
class HTML
|
5
|
+
|
6
|
+
module Numbering # :nodoc:
|
7
|
+
|
8
|
+
def self.number! output, mode = :table, options = {}
|
9
|
+
return self unless mode
|
10
|
+
|
11
|
+
options = DEFAULT_OPTIONS.merge options
|
12
|
+
|
13
|
+
start = options[:line_number_start]
|
14
|
+
unless start.is_a? Integer
|
15
|
+
raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start
|
16
|
+
end
|
17
|
+
|
18
|
+
anchor_prefix = options[:line_number_anchors]
|
19
|
+
anchor_prefix = 'line' if anchor_prefix == true
|
20
|
+
anchor_prefix = anchor_prefix.to_s[/\w+/] if anchor_prefix
|
21
|
+
anchoring =
|
22
|
+
if anchor_prefix
|
23
|
+
proc do |line|
|
24
|
+
line = line.to_s
|
25
|
+
anchor = anchor_prefix + line
|
26
|
+
"<a href=\"##{anchor}\" name=\"#{anchor}\">#{line}</a>"
|
27
|
+
end
|
28
|
+
else
|
29
|
+
proc { |line| line.to_s } # :to_s.to_proc in Ruby 1.8.7+
|
30
|
+
end
|
31
|
+
|
32
|
+
bold_every = options[:bold_every]
|
33
|
+
highlight_lines = options[:highlight_lines]
|
34
|
+
bolding =
|
35
|
+
if bold_every == false && highlight_lines == nil
|
36
|
+
anchoring
|
37
|
+
elsif highlight_lines.is_a? Enumerable
|
38
|
+
highlight_lines = highlight_lines.to_set
|
39
|
+
proc do |line|
|
40
|
+
if highlight_lines.include? line
|
41
|
+
"<strong class=\"highlighted\">#{anchoring[line]}</strong>" # highlighted line numbers in bold
|
42
|
+
else
|
43
|
+
anchoring[line]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
elsif bold_every.is_a? Integer
|
47
|
+
raise ArgumentError, ":bolding can't be 0." if bold_every == 0
|
48
|
+
proc do |line|
|
49
|
+
if line % bold_every == 0
|
50
|
+
"<strong>#{anchoring[line]}</strong>" # every bold_every-th number in bold
|
51
|
+
else
|
52
|
+
anchoring[line]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
else
|
56
|
+
raise ArgumentError, 'Invalid value %p for :bolding; false or Integer expected.' % bold_every
|
57
|
+
end
|
58
|
+
|
59
|
+
line_count = output.count("\n")
|
60
|
+
position_of_last_newline = output.rindex(RUBY_VERSION >= '1.9' ? /\n/ : ?\n)
|
61
|
+
if position_of_last_newline
|
62
|
+
after_last_newline = output[position_of_last_newline + 1 .. -1]
|
63
|
+
ends_with_newline = after_last_newline[/\A(?:<\/span>)*\z/]
|
64
|
+
line_count += 1 if not ends_with_newline
|
65
|
+
end
|
66
|
+
|
67
|
+
case mode
|
68
|
+
when :inline
|
69
|
+
max_width = (start + line_count).to_s.size
|
70
|
+
line_number = start
|
71
|
+
nesting = []
|
72
|
+
output.gsub!(/^.*$\n?/) do |line|
|
73
|
+
line.chomp!
|
74
|
+
open = nesting.join
|
75
|
+
line.scan(%r!<(/)?span[^>]*>?!) do |close,|
|
76
|
+
if close
|
77
|
+
nesting.pop
|
78
|
+
else
|
79
|
+
nesting << $&
|
80
|
+
end
|
81
|
+
end
|
82
|
+
close = '</span>' * nesting.size
|
83
|
+
|
84
|
+
line_number_text = bolding.call line_number
|
85
|
+
indent = ' ' * (max_width - line_number.to_s.size) # TODO: Optimize (10^x)
|
86
|
+
line_number += 1
|
87
|
+
"<span class=\"line-numbers\">#{indent}#{line_number_text}</span>#{open}#{line}#{close}\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
when :table
|
91
|
+
line_numbers = (start ... start + line_count).map(&bolding).join("\n")
|
92
|
+
line_numbers << "\n"
|
93
|
+
line_numbers_table_template = Output::TABLE.apply('LINE_NUMBERS', line_numbers)
|
94
|
+
|
95
|
+
output.gsub!(/<\/div>\n/, '</div>')
|
96
|
+
output.wrap_in! line_numbers_table_template
|
97
|
+
output.wrapped_in = :div
|
98
|
+
|
99
|
+
when :list
|
100
|
+
raise NotImplementedError, 'The :list option is no longer available. Use :table.'
|
101
|
+
|
102
|
+
else
|
103
|
+
raise ArgumentError, 'Unknown value %p for mode: expected one of %p' %
|
104
|
+
[mode, [:table, :inline]]
|
105
|
+
end
|
106
|
+
|
107
|
+
output
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -3,44 +3,29 @@ module Encoders
|
|
3
3
|
|
4
4
|
class HTML
|
5
5
|
|
6
|
-
# This module is included in the output String
|
6
|
+
# This module is included in the output String of the HTML Encoder.
|
7
7
|
#
|
8
8
|
# It provides methods like wrap, div, page etc.
|
9
9
|
#
|
10
10
|
# Remember to use #clone instead of #dup to keep the modules the object was
|
11
11
|
# extended with.
|
12
12
|
#
|
13
|
-
# TODO:
|
13
|
+
# TODO: Rewrite this without monkey patching.
|
14
14
|
module Output
|
15
15
|
|
16
|
-
require 'coderay/encoders/html/numerization.rb'
|
17
|
-
|
18
16
|
attr_accessor :css
|
19
17
|
|
20
18
|
class << self
|
21
19
|
|
22
|
-
# This makes Output look like a class.
|
23
|
-
#
|
24
|
-
# Example:
|
25
|
-
#
|
26
|
-
# a = Output.new '<span class="co">Code</span>'
|
27
|
-
# a.wrap! :page
|
28
|
-
def new string, css = CSS.new, element = nil
|
29
|
-
output = string.clone.extend self
|
30
|
-
output.wrapped_in = element
|
31
|
-
output.css = css
|
32
|
-
output
|
33
|
-
end
|
34
|
-
|
35
20
|
# Raises an exception if an object that doesn't respond to to_str is extended by Output,
|
36
21
|
# to prevent users from misuse. Use Module#remove_method to disable.
|
37
|
-
def extended o
|
22
|
+
def extended o # :nodoc:
|
38
23
|
warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str
|
39
24
|
end
|
40
25
|
|
41
|
-
def make_stylesheet css, in_tag = false
|
26
|
+
def make_stylesheet css, in_tag = false # :nodoc:
|
42
27
|
sheet = css.stylesheet
|
43
|
-
sheet = <<-CSS if in_tag
|
28
|
+
sheet = <<-'CSS' if in_tag
|
44
29
|
<style type="text/css">
|
45
30
|
#{sheet}
|
46
31
|
</style>
|
@@ -48,27 +33,13 @@ module Encoders
|
|
48
33
|
sheet
|
49
34
|
end
|
50
35
|
|
51
|
-
def page_template_for_css css
|
36
|
+
def page_template_for_css css # :nodoc:
|
52
37
|
sheet = make_stylesheet css
|
53
38
|
PAGE.apply 'CSS', sheet
|
54
39
|
end
|
55
40
|
|
56
|
-
# Define a new wrapper. This is meta programming.
|
57
|
-
def wrapper *wrappers
|
58
|
-
wrappers.each do |wrapper|
|
59
|
-
define_method wrapper do |*args|
|
60
|
-
wrap wrapper, *args
|
61
|
-
end
|
62
|
-
define_method "#{wrapper}!".to_sym do |*args|
|
63
|
-
wrap! wrapper, *args
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
41
|
end
|
69
42
|
|
70
|
-
wrapper :div, :span, :page
|
71
|
-
|
72
43
|
def wrapped_in? element
|
73
44
|
wrapped_in == element
|
74
45
|
end
|
@@ -78,10 +49,6 @@ module Encoders
|
|
78
49
|
end
|
79
50
|
attr_writer :wrapped_in
|
80
51
|
|
81
|
-
def wrap_in template
|
82
|
-
clone.wrap_in! template
|
83
|
-
end
|
84
|
-
|
85
52
|
def wrap_in! template
|
86
53
|
Template.wrap! self, template, 'CONTENT'
|
87
54
|
self
|
@@ -118,15 +85,13 @@ module Encoders
|
|
118
85
|
self
|
119
86
|
end
|
120
87
|
|
121
|
-
def wrap *args
|
122
|
-
clone.wrap!(*args)
|
123
|
-
end
|
124
|
-
|
125
88
|
def stylesheet in_tag = false
|
126
89
|
Output.make_stylesheet @css, in_tag
|
127
90
|
end
|
128
91
|
|
129
|
-
|
92
|
+
#-- don't include the templates in docu
|
93
|
+
|
94
|
+
class Template < String # :nodoc:
|
130
95
|
|
131
96
|
def self.wrap! str, template, target
|
132
97
|
target = Regexp.new(Regexp.escape("<%#{target}%>"))
|
@@ -147,47 +112,34 @@ module Encoders
|
|
147
112
|
end
|
148
113
|
end
|
149
114
|
|
150
|
-
module Simple
|
151
|
-
def ` str #` <-- for stupid editors
|
152
|
-
Template.new str
|
153
|
-
end
|
154
|
-
end
|
155
115
|
end
|
156
116
|
|
157
|
-
|
117
|
+
SPAN = Template.new '<span class="CodeRay"><%CONTENT%></span>'
|
158
118
|
|
159
|
-
|
160
|
-
|
161
|
-
SPAN = `<span class="CodeRay"><%CONTENT%></span>`
|
162
|
-
|
163
|
-
DIV = <<-`DIV`
|
119
|
+
DIV = Template.new <<-DIV
|
164
120
|
<div class="CodeRay">
|
165
121
|
<div class="code"><pre><%CONTENT%></pre></div>
|
166
122
|
</div>
|
167
123
|
DIV
|
168
124
|
|
169
|
-
TABLE =
|
125
|
+
TABLE = Template.new <<-TABLE
|
170
126
|
<table class="CodeRay"><tr>
|
171
|
-
<td class="
|
172
|
-
<td class="code"><pre
|
127
|
+
<td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><%LINE_NUMBERS%></pre></td>
|
128
|
+
<td class="code"><pre><%CONTENT%></pre></td>
|
173
129
|
</tr></table>
|
174
130
|
TABLE
|
175
|
-
# title="double click to expand"
|
176
|
-
|
177
|
-
LIST = <<-`LIST`
|
178
|
-
<ol class="CodeRay">
|
179
|
-
<%CONTENT%>
|
180
|
-
</ol>
|
181
|
-
LIST
|
182
131
|
|
183
|
-
PAGE =
|
184
|
-
<!DOCTYPE html
|
185
|
-
|
186
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="de">
|
132
|
+
PAGE = Template.new <<-PAGE
|
133
|
+
<!DOCTYPE html>
|
134
|
+
<html>
|
187
135
|
<head>
|
188
|
-
<meta http-equiv="
|
136
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
189
137
|
<title></title>
|
190
138
|
<style type="text/css">
|
139
|
+
.CodeRay .line-numbers a {
|
140
|
+
text-decoration: inherit;
|
141
|
+
color: inherit;
|
142
|
+
}
|
191
143
|
<%CSS%>
|
192
144
|
</style>
|
193
145
|
</head>
|
@@ -1,69 +1,83 @@
|
|
1
|
-
($:.unshift '../..'; require 'coderay') unless defined? CodeRay
|
2
1
|
module CodeRay
|
3
2
|
module Encoders
|
4
3
|
|
5
|
-
#
|
4
|
+
# A simple JSON Encoder.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
# CodeRay.scan('puts "Hello world!"', :ruby).json
|
8
|
+
# yields
|
9
|
+
# [
|
10
|
+
# {"type"=>"text", "text"=>"puts", "kind"=>"ident"},
|
11
|
+
# {"type"=>"text", "text"=>" ", "kind"=>"space"},
|
12
|
+
# {"type"=>"block", "action"=>"open", "kind"=>"string"},
|
13
|
+
# {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
|
14
|
+
# {"type"=>"text", "text"=>"Hello world!", "kind"=>"content"},
|
15
|
+
# {"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
|
16
|
+
# {"type"=>"block", "action"=>"close", "kind"=>"string"},
|
17
|
+
# ]
|
6
18
|
class JSON < Encoder
|
7
19
|
|
20
|
+
begin
|
21
|
+
require 'json'
|
22
|
+
rescue LoadError
|
23
|
+
begin
|
24
|
+
require 'rubygems' unless defined? Gem
|
25
|
+
gem 'json'
|
26
|
+
require 'json'
|
27
|
+
rescue LoadError
|
28
|
+
$stderr.puts "The JSON encoder needs the JSON library.\n" \
|
29
|
+
"Please gem install json."
|
30
|
+
raise
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
8
34
|
register_for :json
|
9
35
|
FILE_EXTENSION = 'json'
|
10
36
|
|
11
37
|
protected
|
12
38
|
def setup options
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
39
|
+
super
|
40
|
+
|
41
|
+
@first = true
|
42
|
+
@out << '['
|
43
|
+
end
|
44
|
+
|
45
|
+
def finish options
|
46
|
+
@out << ']'
|
47
|
+
end
|
48
|
+
|
49
|
+
def append data
|
50
|
+
if @first
|
51
|
+
@first = false
|
52
|
+
else
|
53
|
+
@out << ','
|
18
54
|
end
|
19
|
-
|
55
|
+
|
56
|
+
@out << data.to_json
|
20
57
|
end
|
21
58
|
|
59
|
+
public
|
22
60
|
def text_token text, kind
|
23
|
-
|
61
|
+
append :type => 'text', :text => text, :kind => kind
|
24
62
|
end
|
25
63
|
|
26
|
-
def
|
27
|
-
|
64
|
+
def begin_group kind
|
65
|
+
append :type => 'block', :action => 'open', :kind => kind
|
28
66
|
end
|
29
67
|
|
30
|
-
def
|
31
|
-
|
68
|
+
def end_group kind
|
69
|
+
append :type => 'block', :action => 'close', :kind => kind
|
70
|
+
end
|
71
|
+
|
72
|
+
def begin_line kind
|
73
|
+
append :type => 'block', :action => 'begin_line', :kind => kind
|
74
|
+
end
|
75
|
+
|
76
|
+
def end_line kind
|
77
|
+
append :type => 'block', :action => 'end_line', :kind => kind
|
32
78
|
end
|
33
79
|
|
34
80
|
end
|
35
81
|
|
36
82
|
end
|
37
83
|
end
|
38
|
-
|
39
|
-
if $0 == __FILE__
|
40
|
-
$VERBOSE = true
|
41
|
-
$: << File.join(File.dirname(__FILE__), '..')
|
42
|
-
eval DATA.read, nil, $0, __LINE__ + 4
|
43
|
-
end
|
44
|
-
|
45
|
-
__END__
|
46
|
-
require 'test/unit'
|
47
|
-
$:.delete '.'
|
48
|
-
require 'rubygems' if RUBY_VERSION < '1.9'
|
49
|
-
|
50
|
-
class JSONEncoderTest < Test::Unit::TestCase
|
51
|
-
|
52
|
-
def test_json_output
|
53
|
-
tokens = CodeRay.scan <<-RUBY, :ruby
|
54
|
-
puts "Hello world!"
|
55
|
-
RUBY
|
56
|
-
require 'json'
|
57
|
-
assert_equal [
|
58
|
-
{"type"=>"text", "text"=>"puts", "kind"=>"ident"},
|
59
|
-
{"type"=>"text", "text"=>" ", "kind"=>"space"},
|
60
|
-
{"type"=>"block", "action"=>"open", "kind"=>"string"},
|
61
|
-
{"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
|
62
|
-
{"type"=>"text", "text"=>"Hello world!", "kind"=>"content"},
|
63
|
-
{"type"=>"text", "text"=>"\"", "kind"=>"delimiter"},
|
64
|
-
{"type"=>"block", "action"=>"close", "kind"=>"string"},
|
65
|
-
{"type"=>"text", "text"=>"\n", "kind"=>"space"}
|
66
|
-
], JSON.load(tokens.json)
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|