pry-coolline 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
-