pry-coolline 0.1.5 → 0.2.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/.gemtest CHANGED
File without changes
data/.gitignore CHANGED
@@ -1,7 +1,7 @@
1
- Makefile
2
- *.so
3
- *.o
4
- *.def
5
- doc/
6
- pkg/
7
- .yardoc/
1
+ Makefile
2
+ *.so
3
+ *.o
4
+ *.def
5
+ doc/
6
+ pkg/
7
+ .yardoc/
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup markdown
1
+ --markup markdown
data/CHANGELOG CHANGED
File without changes
data/LICENSE CHANGED
@@ -1,25 +1,25 @@
1
- License
2
- -------
3
-
4
- (The MIT License)
5
-
6
- Copyright (c) 2012 John Mair (banisterfiend)
7
-
8
- Permission is hereby granted, free of charge, to any person obtaining
9
- a copy of this software and associated documentation files (the
10
- 'Software'), to deal in the Software without restriction, including
11
- without limitation the rights to use, copy, modify, merge, publish,
12
- distribute, sublicense, and/or sell copies of the Software, and to
13
- permit persons to whom the Software is furnished to do so, subject to
14
- the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be
17
- included in all copies or substantial portions of the Software.
18
-
19
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ License
2
+ -------
3
+
4
+ (The MIT License)
5
+
6
+ Copyright (c) 2012 John Mair (banisterfiend)
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining
9
+ a copy of this software and associated documentation files (the
10
+ 'Software'), to deal in the Software without restriction, including
11
+ without limitation the rights to use, copy, modify, merge, publish,
12
+ distribute, sublicense, and/or sell copies of the Software, and to
13
+ permit persons to whom the Software is furnished to do so, subject to
14
+ the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be
17
+ included in all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -20,26 +20,33 @@ gem and the `io/console` library.
20
20
  After installing `pry-coolline` just start Pry as normal, the coolline
21
21
  plugin will be detected and used automatically.
22
22
 
23
- **Known Issues:**
24
-
25
- On some platforms the up and down cursor keys do not work properly; to
26
- get around this you can use the emacs keybindings.
23
+ <center>
24
+ ![Alt text](http://dl.dropbox.com/u/26521875/coolline.png)
27
25
 
28
- These are:
26
+ Features
27
+ --------
29
28
 
29
+ ***Automatic Ruby syntax highlighting***
30
30
 
31
- `^p` equivalent to up-arrow
31
+ This is the main feature. It is enabled by default, as long as
32
+ `Pry.config.color` is true.
32
33
 
33
- `^n` equivalent to down-arrow
34
+ ***Matching of opening and closing parentheses and brackets***
34
35
 
35
- `^d` equivalent to delete
36
+ `pry-coolline` allows to match parentheses and brackets automatically. This
37
+ behavior can be configured and disabled altogether:
36
38
 
37
- `^e` navigate to end of line
39
+ # Whether or not to enable this feature. Defaults to true.
40
+ Pry.config.coolline_paren_matching = false
38
41
 
39
- `^a` navigate to start of line
42
+ # Change the color code inserted when paprens are (mis)matched.
43
+ Pry.config.coolline_matched_paren = "\e[42m"
44
+ Pry.config.coolline_mismatched_paren = "\e[41m"
40
45
 
41
- <center>
42
- ![Alt text](http://dl.dropbox.com/u/26521875/coolline.png)
46
+ # In case you can't remember ANSI color codes ;)
47
+ require 'term/ansicolor'
48
+ Pry.config.coolline_matched_paren = Term::ANSIColor.on_green
49
+ Pry.config.coolline_mismatched_paren = Term::ANSIColor.on_red
43
50
 
44
51
  Limitations
45
52
  -----------
data/Rakefile CHANGED
@@ -23,6 +23,7 @@ def apply_spec_defaults(s)
23
23
  s.description = s.summary
24
24
  s.add_dependency("coolline","~>0.3")
25
25
  s.add_dependency("io-console","~>0.3.0")
26
+ s.add_development_dependency("riot")
26
27
  s.required_ruby_version = '>= 1.9.2'
27
28
  s.require_path = 'lib'
28
29
  s.homepage = "https://github.com/pry/pry-coolline"
@@ -39,7 +40,7 @@ end
39
40
 
40
41
  desc "run tests"
41
42
  task :test do
42
- sh "bacon -Itest -rubygems -a"
43
+ ruby "test/run_all.rb"
43
44
  end
44
45
 
45
46
  desc "Show version"
@@ -0,0 +1,278 @@
1
+ # -*- coding: utf-8 -*-
2
+ module PryCoolline
3
+ module ParenMatch
4
+ Pairs = {
5
+ "(" => ")",
6
+ "[" => "]",
7
+ "{" => "}",
8
+ }
9
+
10
+ AnsiCode = Coolline::AnsiCode
11
+
12
+ # A Token is a short chunk of code. This tokenizer only distinguishes three
13
+ # kinds of token:
14
+ #
15
+ # 1. Tokens that open a pair (OpenToken)
16
+ # 2. Tokens that close a pair (CloseToken)
17
+ # 3. The rest of the code (StrToken)
18
+ #
19
+ # @attr [String] str String covered by the token
20
+ # @attr [Integer] pos Position in the initial code, character-wise,
21
+ # disregarding ANSI codes.
22
+ # @attr [Integer] code_pos Position in the initial code, character-wise,
23
+ # including ANSI codes.
24
+ Token = Struct.new(:str, :pos, :code_pos)
25
+
26
+ OpenToken = Class.new(Token)
27
+ CloseToken = Class.new(Token)
28
+ StrToken = Class.new(Token)
29
+
30
+ # Represents a pair that should be matched.
31
+ #
32
+ # @attr [OpenToken, nil] open
33
+ # @attr [CloseToken, nil] close
34
+ Pair = Struct.new(:open, :close) do
35
+ # @return [Boolean] True if the opening character and the closing
36
+ # characters are both present and match correctly.
37
+ def correctly_matched?(pairs = Pairs)
38
+ open and close and pairs[open.str] == close.str
39
+ end
40
+
41
+ # @return [Array<Array<Integer>>] Positions where the user can insert
42
+ # color indicators, in descending order so that String#insert can be
43
+ # used safely.
44
+ #
45
+ # @example
46
+ # insertion_positions.each do |before, after|
47
+ # string.insert after, clear_code
48
+ # string.insert before, background_code
49
+ # end
50
+ def insertion_positions
51
+ ary = []
52
+
53
+ if close
54
+ ary << [close.code_pos, close.code_pos + close.str.size]
55
+ end
56
+
57
+ if open
58
+ ary << [open.code_pos, open.code_pos + open.str.size]
59
+ end
60
+
61
+ ary
62
+ end
63
+ end
64
+
65
+ # This module contains the different kinds of AST nodes generated when
66
+ # trying to match pairs of opening and closing characters.
67
+ #
68
+ module AST
69
+ # The root of the AST tree.
70
+ # @attr [Array<Leaf, DanglingClose, Node>] elements All the top-level
71
+ # nodes of the tree.
72
+ Root = Struct.new(:elements) do
73
+ # @param [Parser] parser Parser to get the tokens from
74
+ def initialize(parser)
75
+ self.elements = []
76
+
77
+ while tok = parser.next_token
78
+ case tok
79
+ when OpenToken then elements << Node.new(parser, tok)
80
+ when StrToken then elements << Leaf.new(tok)
81
+ when CloseToken then elements << DanglingClose.new(tok)
82
+ end
83
+ end
84
+ end
85
+
86
+ # Finds the opening and closing tokens that should be matched at a
87
+ # certain position in the string.
88
+ #
89
+ # It is assumed you can be looking for the closing parenthesis when on
90
+ # the opening one, or for the opening one when selecting the character
91
+ # that immediately follows it.
92
+ #
93
+ # @param [Integer] pos
94
+ #
95
+ # @return [Pair] An (open, close) pair. Notice both the opening and
96
+ # closing tokens coud be nil.
97
+ def pair_at(pos)
98
+ elements.each do |el|
99
+ if pair = el.pair_at(pos)
100
+ return pair
101
+ end
102
+ end
103
+
104
+ Pair.new
105
+ end
106
+ end
107
+
108
+ # A sequence of text on its own, that contains no parts of a pair.
109
+ #
110
+ # @attr [StrToken] tok
111
+ Leaf = Struct.new(:tok) do
112
+ # (see Root#pair_at)
113
+ def pair_at(pos)
114
+ nil
115
+ end
116
+ end
117
+
118
+ # A top-level closing paren — meaning there's no matching opening
119
+ # character.
120
+ DanglingClose = Struct.new(:tok) do
121
+ # (see Root#pair_at)
122
+ def pair_at(pos)
123
+ if pos == tok.pos + tok.str.size
124
+ Pair.new(nil, tok)
125
+ end
126
+ end
127
+ end
128
+
129
+ # A block of code that was opened by a character, and may or may not (but
130
+ # should!) have been closed by another token.
131
+ #
132
+ # @attr [OpenToken] open
133
+ # @attr [CloseToken, nil] close
134
+ #
135
+ # @attr [Array<Leaf, Node>] elements Other nodes within the opening and
136
+ # the closing tokens.
137
+ Node = Struct.new(:open, :elements, :close) do
138
+ def initialize(parser, open)
139
+ self.elements = []
140
+ self.open = open
141
+
142
+ while tok = parser.next_token and !tok.is_a?(CloseToken)
143
+ case tok
144
+ when OpenToken then elements << Node.new(parser, tok)
145
+ when StrToken then elements << Leaf.new(tok)
146
+ end
147
+ end
148
+
149
+ self.close = tok # might be nil, which is okay
150
+ end
151
+
152
+ # (see Root#pair_at)
153
+ def pair_at(pos)
154
+ if pos == open.pos ||
155
+ (close && pos == close.pos + close.str.size)
156
+ Pair.new(open, close)
157
+ else
158
+ elements.each do |el|
159
+ if pair = el.pair_at(pos)
160
+ return pair
161
+ end
162
+ end
163
+
164
+ nil
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ class Parser
171
+ # @param (see ParenMatch#pair_at)
172
+ # @return [AST::Root]
173
+ def self.parse(code, pairs = Pairs)
174
+ new(code, pairs).parse
175
+ end
176
+
177
+ # @param (see ParenMatch#pair_at)
178
+ def initialize(code, pairs = Pairs)
179
+ @tokens = tokenize(code, pairs)
180
+ end
181
+
182
+ # @return [AST::Root]
183
+ def parse
184
+ AST::Root.new self
185
+ end
186
+
187
+ # Returns the next token found in the array and removes it.
188
+ #
189
+ # @return [Token] The next token, which will be removed from the queue.
190
+ def next_token
191
+ @tokens.shift
192
+ end
193
+
194
+ private
195
+
196
+ # Generates token from a given chunk of code.
197
+ #
198
+ # ANSI codes are ignored, as if they were comments.
199
+ #
200
+ # @param (see ParenMatch#pair_at)
201
+ # @return [Array<Token>]
202
+ def tokenize(code, pairs)
203
+ openers = Regexp.union(pairs.keys)
204
+ closers = Regexp.union(pairs.values)
205
+
206
+ token = Regexp.union(openers, closers, AnsiCode)
207
+
208
+ tokens = []
209
+
210
+ char_pos = pos = 0
211
+
212
+ until pos == code.size
213
+ old_pos = pos
214
+
215
+ if m = match_at(code, AnsiCode, pos)
216
+ pos = m.end(0)
217
+ elsif m = match_at(code, openers, pos)
218
+ tokens << OpenToken.new(m.to_s, char_pos, pos)
219
+ pos = m.end(0)
220
+ char_pos += pos - old_pos
221
+ elsif m = match_at(code, closers, pos)
222
+ tokens << CloseToken.new(m.to_s, char_pos, pos)
223
+ pos = m.end(0)
224
+ char_pos += pos - old_pos
225
+ elsif m = code.match(token, pos)
226
+ tokens << StrToken.new(code[pos...m.begin(0)], char_pos, pos)
227
+ pos = m.begin(0)
228
+ char_pos += pos - old_pos
229
+ else
230
+ tokens << StrToken.new(code[pos..-1], char_pos, pos)
231
+ pos = code.size
232
+ end
233
+ end
234
+
235
+ tokens
236
+ end
237
+
238
+ # @return [Matchdata, nil] Matchdata if the regexp matches the string at
239
+ # the specifed position, nil otherwise.
240
+ def match_at(string, regexp, pos)
241
+ if m = string.match(regexp, pos) and m.begin(0) == pos
242
+ m
243
+ end
244
+ end
245
+ end
246
+
247
+ module_function
248
+ # @param [String] code
249
+ # @param [Integer] pos Position of the selected character, disregarding ANSI
250
+ # codes.
251
+ # @param [Hash<String, String>] pairs A hash mapping each opening character
252
+ # to a closing one.
253
+ #
254
+ # @return (see AST::Root#pair_at)
255
+ def pair_at(code, pos, pairs = Pairs)
256
+ Parser.parse(code, pairs).pair_at pos
257
+ end
258
+ end
259
+
260
+ module_function
261
+
262
+ # Adds paren matching code for the parens that are matched at a given
263
+ # position. The color codes are determined using Pry.config.
264
+ #
265
+ # @param [String] code
266
+ # @param [Integer] pos
267
+ def apply_paren_matching(code, pos)
268
+ pair = ParenMatch.pair_at(code, pos)
269
+
270
+ color = pair.correctly_matched? ? Pry.config.coolline_matched_paren :
271
+ Pry.config.coolline_mismatched_paren
272
+
273
+ pair.insertion_positions.each do |before, after|
274
+ code.insert after, "\e[0m"
275
+ code.insert before, color
276
+ end
277
+ end
278
+ end
@@ -1,3 +1,3 @@
1
1
  module PryCoolline
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/pry-coolline.rb CHANGED
@@ -2,26 +2,37 @@
2
2
  # (C) John Mair (banisterfiend); MIT license
3
3
 
4
4
  require 'pry'
5
- require "pry-coolline/version"
5
+ require 'pry-coolline/version'
6
+
7
+ Pry.config.coolline_paren_matching ||= true
8
+
9
+ Pry.config.coolline_matched_paren ||= "\e[42m"
10
+ Pry.config.coolline_mismatched_paren ||= "\e[41m"
6
11
 
7
12
  begin
8
13
  require 'coolline'
14
+ require 'pry-coolline/paren_match'
9
15
 
10
16
  Pry.config.input = Coolline.new do |cool|
11
17
  cool.word_boundaries = [" ", "\t", ",", ";", '"', "'", "`", "<", ">",
12
18
  "=", ";", "|", "{", "}", "(", ")", "-"]
13
19
 
14
20
  # bring saved history into coolline
15
- cool.history_file = Pry.config.history.file
21
+ cool.history_file = File.expand_path(Pry.config.history.file)
16
22
 
17
23
  cool.transform_proc = proc do
18
24
  if Pry.color
19
- CodeRay.scan(cool.line, :ruby).term
25
+ code = CodeRay.scan(cool.line, :ruby).term
26
+
27
+ if Pry.config.coolline_paren_matching
28
+ PryCoolline.apply_paren_matching(code, cool.pos)
29
+ end
30
+
31
+ code
20
32
  else
21
33
  cool.line
22
34
  end
23
35
  end
24
36
  end
25
-
26
37
  rescue LoadError
27
38
  end if ENV["TERM"] != "dumb"
data/test/helpers.rb ADDED
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", File.dirname(__FILE__))
2
+
3
+ require 'riot'
4
+ require 'pry-coolline'
5
+
6
+ Riot.reporter = Riot::PrettyDotMatrixReporter
@@ -0,0 +1,76 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path("helpers.rb", File.dirname(__FILE__))
3
+
4
+ # This code allows to test paren matching against:
5
+ # - multibyte characters;
6
+ # - ANSI codes.
7
+ code = <<eof
8
+ )foo (bar) bat} [béz] () bat] ( 3} [bat {bar}\e[3m] [
9
+ eof
10
+
11
+ PM = PryCoolline::ParenMatch
12
+
13
+ context PM do
14
+ setup { PM }
15
+
16
+ def pair(a = nil, b = nil)
17
+ PM::Pair.new(a ? PM::OpenToken.new(*a) : a,
18
+ b ? PM::CloseToken.new(*b) : b)
19
+ end
20
+
21
+ asserts(:pair_at, code, 0).equals pair
22
+ asserts(:pair_at, code, 1).equals pair(nil, [")", 0, 0])
23
+ asserts(:pair_at, code, 3).equals pair
24
+ asserts(:pair_at, code, 5).equals pair(["(", 5, 5], [")", 9, 9])
25
+ asserts(:pair_at, code, 15).equals pair(nil, ["}", 14, 14])
26
+ asserts(:pair_at, code, 22).equals pair(["(", 22, 22], [")", 23, 23])
27
+ asserts(:pair_at, code, 23).equals pair
28
+ asserts(:pair_at, code, 24).equals pair(["(", 22, 22], [")", 23, 23])
29
+ asserts(:pair_at, code, 30).equals pair(["(", 30, 30], ["}", 33, 33])
30
+ asserts(:pair_at, code, 34).equals pair(["(", 30, 30], ["}", 33, 33])
31
+ asserts(:pair_at, code, 35).equals pair(["[", 35, 35], ["]", 45, 49])
32
+ asserts(:pair_at, code, 40).equals pair(["{", 40, 40], ["}", 44, 44])
33
+ asserts(:pair_at, code, 45).equals pair(["{", 40, 40], ["}", 44, 44])
34
+ asserts(:pair_at, code, 46).equals pair(["[", 35, 35], ["]", 45, 49])
35
+ asserts(:pair_at, code, 47).equals pair(["[", 47, 51])
36
+ end
37
+
38
+ context "a fully matched pair" do
39
+ setup do
40
+ PM::Pair.new(PM::OpenToken.new("abc", 3, 4),
41
+ PM::CloseToken.new("ab", 10, 15))
42
+ end
43
+
44
+ denies(:correctly_matched?)
45
+ denies(:correctly_matched?, "abc" => "cba")
46
+ asserts "correctly matched if pairs are right" do
47
+ topic.correctly_matched? "abc" => "ab"
48
+ end
49
+
50
+ asserts(:insertion_positions).equals [[15, 17], [4, 7]]
51
+ end
52
+
53
+ context "a pair without closing token" do
54
+ setup do
55
+ PM::Pair.new(PM::OpenToken.new("abc", 3, 4))
56
+ end
57
+
58
+ denies(:correctly_matched?)
59
+ asserts(:insertion_positions).equals [[4, 7]]
60
+ end
61
+
62
+ context "a pair without opening token" do
63
+ setup do
64
+ PM::Pair.new(nil, PM::CloseToken.new("ab", 10, 15))
65
+ end
66
+
67
+ denies(:correctly_matched?)
68
+ asserts(:insertion_positions).equals [[15, 17]]
69
+ end
70
+
71
+ context "an empty pair" do
72
+ setup { PryCoolline::ParenMatch::Pair.new }
73
+
74
+ denies(:correctly_matched?)
75
+ asserts(:insertion_positions).empty
76
+ end
data/test/run_all.rb ADDED
@@ -0,0 +1,5 @@
1
+ require File.expand_path("helpers.rb", File.dirname(__FILE__))
2
+
3
+ Dir.glob(File.join(File.dirname(__FILE__), "**/*_test.rb")) do |file|
4
+ load file
5
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pry-coolline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-13 00:00:00.000000000 Z
12
+ date: 2013-01-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: coolline
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: 0.3.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: riot
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
46
62
  description: Live syntax-highlighting for the Pry REPL
47
63
  email: jrmair@gmail.com
48
64
  executables: []
@@ -57,9 +73,12 @@ files:
57
73
  - README.md
58
74
  - Rakefile
59
75
  - lib/pry-coolline.rb
76
+ - lib/pry-coolline/paren_match.rb
60
77
  - lib/pry-coolline/version.rb
61
78
  - pry-coolline.gemspec
62
- - test/test.rb
79
+ - test/helpers.rb
80
+ - test/paren_match_test.rb
81
+ - test/run_all.rb
63
82
  homepage: https://github.com/pry/pry-coolline
64
83
  licenses: []
65
84
  post_install_message:
@@ -80,9 +99,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
99
  version: '0'
81
100
  requirements: []
82
101
  rubyforge_project:
83
- rubygems_version: 1.8.24
102
+ rubygems_version: 1.8.23
84
103
  signing_key:
85
104
  specification_version: 3
86
105
  summary: Live syntax-highlighting for the Pry REPL
87
106
  test_files:
88
- - test/test.rb
107
+ - test/helpers.rb
108
+ - test/paren_match_test.rb
109
+ - test/run_all.rb
data/test/test.rb DELETED
@@ -1,12 +0,0 @@
1
- direc = File.dirname(__FILE__)
2
-
3
- require 'rubygems'
4
- require "#{direc}/../lib/pry-coolline"
5
- require 'bacon'
6
-
7
- puts "Testing pry-coolline version #{PryCoolline::VERSION}..."
8
- puts "Ruby version: #{RUBY_VERSION}"
9
-
10
- describe PryCoolline do
11
- end
12
-