syntax_suggest 1.0.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f8b41fd2cbb19ed9ce0553b53e88f29de1a0ae46e7ffbd0de1d9bf1b881a9a5
4
- data.tar.gz: c0787c1da43c9e19ca97e8fd616360c188946e996c283aa8c0b4c187dd2a66ec
3
+ metadata.gz: 20590130dde6c0a40a17b7d01ad9fca063c7044c83d7f04609f9d75df564f9ce
4
+ data.tar.gz: 73bed388644be5a7a71fe5ab892c8925d50f9dbcad5d918ca07512ffd85188af
5
5
  SHA512:
6
- metadata.gz: 9ed334240baa6233e259756bd4e532638fb7796e5fc3f671a3c31bb8c01a508d1418fe62380f3c5db9bb1568395b2686ab9fb56c4badcace009b91d92c2cbded
7
- data.tar.gz: 5fa487311079e5000a2bbfb8216e855983c816668657e7f4fbca4fe2e79370e6c00e1665d4230879ca59c2ec4c9476a387c2295aa1b13d259d41446ae84c8629
6
+ metadata.gz: 42a28115779d75b9ff89e58b2a26a9c4807e834a91f8671ffdb2725f9dea954b017310c3b67a9bfe7b97ea6bdbb28e84c59f8bf5c951b0692e99f74ab3e9a5a3
7
+ data.tar.gz: 51c04529ff19c2181b7f025ff04165f436afaf22e7c9daffb23767176b48b8216006a9fd669b96a435abd8c9886b046224212e9f6c50435008a8ef677477f771
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'weekly'
@@ -13,7 +13,7 @@ jobs:
13
13
  !contains(github.event.pull_request.body, '[skip ci]') &&
14
14
  !contains(github.event.pull_request.labels.*.name, 'skip changelog')
15
15
  steps:
16
- - uses: actions/checkout@v2.3.5
16
+ - uses: actions/checkout@v3.3.0
17
17
  - name: Check that CHANGELOG is touched
18
18
  run: |
19
19
  git fetch origin ${{ github.base_ref }} --depth 1 && \
@@ -9,7 +9,7 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - name: Checkout code
12
- uses: actions/checkout@v3
12
+ uses: actions/checkout@v3.3.0
13
13
  - name: Set up Ruby
14
14
  uses: ruby/setup-ruby@v1
15
15
  with:
@@ -29,16 +29,16 @@ jobs:
29
29
  - 2.7
30
30
  - '3.0'
31
31
  - 3.1
32
- - "3.2.0-preview1"
32
+ - 3.2
33
33
  - head
34
34
  steps:
35
35
  - name: Checkout code
36
- uses: actions/checkout@v3
36
+ uses: actions/checkout@v3.3.0
37
37
  - name: Set up Ruby
38
38
  uses: ruby/setup-ruby@v1
39
39
  with:
40
40
  ruby-version: ${{ matrix.ruby }}
41
41
  bundler-cache: true
42
42
  - name: test
43
- run: bundle exec rake test
43
+ run: bin/rake test
44
44
  continue-on-error: ${{ matrix.ruby == 'head' }}
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --format documentation
2
- --color
3
2
  --require spec_helper
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## HEAD (unreleased)
2
2
 
3
+ ## 1.0.3
4
+
5
+ - Output improvement: Handle methods with only newlines or comments in them (https://github.com/ruby/syntax_suggest/pull/179)
6
+ - No longer shows the detail of monkey patch as the document (https://github.com/ruby/syntax_suggest/pull/174)
7
+ - Drop CI for Ruby 3.2.0-rc1, now that 3.2.0 is available (https://github.com/ruby/syntax_suggest/pull/172)
8
+
9
+ ## 1.0.2
10
+
11
+ - Drop support for Ruby 3.2.0 preview, now that 3.2.0-rc1 is available (https://github.com/ruby/syntax_suggest/pull/165)
12
+ - Native support of `SyntaxError#path`, support 3.2.0-preview3 will be dropped with the release of 3.2.0-preview4 (https://github.com/ruby/syntax_suggest/pull/164)
13
+ - Added dependabot for GitHub Actions (https://github.com/ruby/syntax_suggest/pull/160)
14
+
3
15
  ## 1.0.1
4
16
 
5
17
  - Replace `❯` with `>` in error output for compatability with more fonts (https://github.com/ruby/syntax_suggest/pull/161)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- syntax_suggest (1.0.1)
4
+ syntax_suggest (1.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -180,9 +180,20 @@ Any other entrypoints are subject to change without warning. If you want to use
180
180
 
181
181
  ## Development
182
182
 
183
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
183
+ ### Handling conflicts with the default gem
184
184
 
185
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
185
+ Because `syntax_suggest` is a default gem you can get conflicts when working on this project with Ruby 3.2+. To fix conflicts you can disable loading `syntax_suggest` as a default gem by using then environment variable `RUBYOPT` with the value `--disable=syntax_suggest`. The `RUBYOPT` environment variable works the same as if we had entered those flags directly in the ruby cli (i.e. `ruby --disable=syntax_suggest` is the same as `RUBYOPT="--disable=syntax_suggest" ruby`). It's needed because we don't always directly execute Ruby and RUBYOPT will be picked up when other commands load ruby (`rspec`, `rake`, or `bundle` etc.).
186
+
187
+ There are some binstubs that already have this done for you. Instead of running `bundle exec rake` you can run `bin/rake`. Binstubs provided:
188
+
189
+ - `bin/rake`
190
+ - `bin/rspec`
191
+
192
+ ### Installation
193
+
194
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
195
+
196
+ To install this gem onto your local machine, run `bin/rake install`. To release a new version, update the version number in `version.rb`, and then run `bin/rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
186
197
 
187
198
  ### How to debug changes to output display
188
199
 
data/bin/rake ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Make sure syntax_suggest default gem is not loaded first
5
+ RUBYOPT="${RUBYOPT-} --disable=syntax_suggest"
6
+ RUBYOPT="$RUBYOPT" bundle exec rake "$@"
data/bin/rspec ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Make sure syntax_suggest default gem is not loaded first
5
+ RUBYOPT="${RUBYOPT-} --disable=syntax_suggest"
6
+ RUBYOPT="$RUBYOPT" bundle exec rspec "$@"
@@ -91,7 +91,9 @@ module SyntaxSuggest
91
91
  dir = Pathname(dir)
92
92
  dir.join(time).tap { |path|
93
93
  path.mkpath
94
- FileUtils.ln_sf(time, dir.join("last"))
94
+ alias_dir = dir.join("last")
95
+ FileUtils.rm_rf(alias_dir) if alias_dir.exist?
96
+ FileUtils.ln_sf(time, alias_dir)
95
97
  }
96
98
  end
97
99
 
@@ -38,36 +38,64 @@ module SyntaxSuggest
38
38
  @before_array = []
39
39
  @stop_after_kw = false
40
40
 
41
- @skip_hidden = false
42
- @skip_empty = false
43
- end
44
-
45
- def skip(name)
46
- case name
47
- when :hidden?
48
- @skip_hidden = true
49
- when :empty?
50
- @skip_empty = true
51
- else
52
- raise "Unsupported skip #{name}"
53
- end
41
+ @force_add_hidden = false
42
+ @force_add_empty = false
43
+ end
44
+
45
+ # When using this flag, `scan_while` will
46
+ # bypass the block it's given and always add a
47
+ # line that responds truthy to `CodeLine#hidden?`
48
+ #
49
+ # Lines are hidden when they've been evaluated by
50
+ # the parser as part of a block and found to contain
51
+ # valid code.
52
+ def force_add_hidden
53
+ @force_add_hidden = true
54
+ self
55
+ end
56
+
57
+ # When using this flag, `scan_while` will
58
+ # bypass the block it's given and always add a
59
+ # line that responds truthy to `CodeLine#empty?`
60
+ #
61
+ # Empty lines contain no code, only whitespace such
62
+ # as leading spaces a newline.
63
+ def force_add_empty
64
+ @force_add_empty = true
54
65
  self
55
66
  end
56
67
 
68
+ # Tells `scan_while` to look for mismatched keyword/end-s
69
+ #
70
+ # When scanning up, if we see more keywords then end-s it will
71
+ # stop. This might happen when scanning outside of a method body.
72
+ # the first scan line up would be a keyword and this setting would
73
+ # trigger a stop.
74
+ #
75
+ # When scanning down, stop if there are more end-s than keywords.
57
76
  def stop_after_kw
58
77
  @stop_after_kw = true
59
78
  self
60
79
  end
61
80
 
81
+ # Main work method
82
+ #
83
+ # The scan_while method takes a block that yields lines above and
84
+ # below the block. If the yield returns true, the @before_index
85
+ # or @after_index are modified to include the matched line.
86
+ #
87
+ # In addition to yielding individual lines, the internals of this
88
+ # object give a mini DSL to handle common situations such as
89
+ # stopping if we've found a keyword/end mis-match in one direction
90
+ # or the other.
62
91
  def scan_while
63
92
  stop_next = false
64
-
65
93
  kw_count = 0
66
94
  end_count = 0
67
95
  index = before_lines.reverse_each.take_while do |line|
68
96
  next false if stop_next
69
- next true if @skip_hidden && line.hidden?
70
- next true if @skip_empty && line.empty?
97
+ next true if @force_add_hidden && line.hidden?
98
+ next true if @force_add_empty && line.empty?
71
99
 
72
100
  kw_count += 1 if line.is_kw?
73
101
  end_count += 1 if line.is_end?
@@ -87,8 +115,8 @@ module SyntaxSuggest
87
115
  end_count = 0
88
116
  index = after_lines.take_while do |line|
89
117
  next false if stop_next
90
- next true if @skip_hidden && line.hidden?
91
- next true if @skip_empty && line.empty?
118
+ next true if @force_add_hidden && line.hidden?
119
+ next true if @force_add_empty && line.empty?
92
120
 
93
121
  kw_count += 1 if line.is_kw?
94
122
  end_count += 1 if line.is_end?
@@ -105,6 +133,33 @@ module SyntaxSuggest
105
133
  self
106
134
  end
107
135
 
136
+ # Shows surrounding kw/end pairs
137
+ #
138
+ # The purpose of showing these extra pairs is due to cases
139
+ # of ambiguity when only one visible line is matched.
140
+ #
141
+ # For example:
142
+ #
143
+ # 1 class Dog
144
+ # 2 def bark
145
+ # 4 def eat
146
+ # 5 end
147
+ # 6 end
148
+ #
149
+ # In this case either line 2 could be missing an `end` or
150
+ # line 4 was an extra line added by mistake (it happens).
151
+ #
152
+ # When we detect the above problem it shows the issue
153
+ # as only being on line 2
154
+ #
155
+ # 2 def bark
156
+ #
157
+ # Showing "neighbor" keyword pairs gives extra context:
158
+ #
159
+ # 2 def bark
160
+ # 4 def eat
161
+ # 5 end
162
+ #
108
163
  def capture_neighbor_context
109
164
  lines = []
110
165
  kw_count = 0
@@ -146,6 +201,20 @@ module SyntaxSuggest
146
201
  lines
147
202
  end
148
203
 
204
+ # Shows the context around code provided by "falling" indentation
205
+ #
206
+ # Converts:
207
+ #
208
+ # it "foo" do
209
+ #
210
+ # into:
211
+ #
212
+ # class OH
213
+ # def hello
214
+ # it "foo" do
215
+ # end
216
+ # end
217
+ #
149
218
  def on_falling_indent
150
219
  last_indent = @orig_indent
151
220
  before_lines.reverse_each do |line|
@@ -166,18 +235,79 @@ module SyntaxSuggest
166
235
  end
167
236
  end
168
237
 
169
- def scan_neighbors
238
+ # Scanning is intentionally conservative because
239
+ # we have no way of rolling back an agressive block (at this time)
240
+ #
241
+ # If a block was stopped for some trivial reason, (like an empty line)
242
+ # but the next line would have caused it to be balanced then we
243
+ # can check that condition and grab just one more line either up or
244
+ # down.
245
+ #
246
+ # For example, below if we're scanning up, line 2 might cause
247
+ # the scanning to stop. This is because empty lines might
248
+ # denote logical breaks where the user intended to chunk code
249
+ # which is a good place to stop and check validity. Unfortunately
250
+ # it also means we might have a "dangling" keyword or end.
251
+ #
252
+ # 1 def bark
253
+ # 2
254
+ # 3 end
255
+ #
256
+ # If lines 2 and 3 are in the block, then when this method is
257
+ # run it would see it is unbalanced, but that acquiring line 1
258
+ # would make it balanced, so that's what it does.
259
+ def lookahead_balance_one_line
260
+ kw_count = 0
261
+ end_count = 0
262
+ lines.each do |line|
263
+ kw_count += 1 if line.is_kw?
264
+ end_count += 1 if line.is_end?
265
+ end
266
+
267
+ return self if kw_count == end_count # nothing to balance
268
+
269
+ # More ends than keywords, check if we can balance expanding up
270
+ if (end_count - kw_count) == 1 && next_up
271
+ return self unless next_up.is_kw?
272
+ return self unless next_up.indent >= @orig_indent
273
+
274
+ @before_index = next_up.index
275
+
276
+ # More keywords than ends, check if we can balance by expanding down
277
+ elsif (kw_count - end_count) == 1 && next_down
278
+ return self unless next_down.is_end?
279
+ return self unless next_down.indent >= @orig_indent
280
+
281
+ @after_index = next_down.index
282
+ end
283
+ self
284
+ end
285
+
286
+ # Finds code lines at the same or greater indentation and adds them
287
+ # to the block
288
+ def scan_neighbors_not_empty
170
289
  scan_while { |line| line.not_empty? && line.indent >= @orig_indent }
171
290
  end
172
291
 
292
+ # Returns the next line to be scanned above the current block.
293
+ # Returns `nil` if at the top of the document already
173
294
  def next_up
174
295
  @code_lines[before_index.pred]
175
296
  end
176
297
 
298
+ # Returns the next line to be scanned below the current block.
299
+ # Returns `nil` if at the bottom of the document already
177
300
  def next_down
178
301
  @code_lines[after_index.next]
179
302
  end
180
303
 
304
+ # Scan blocks based on indentation of next line above/below block
305
+ #
306
+ # Determines indentaion of the next line above/below the current block.
307
+ #
308
+ # Normally this is called when a block has expanded to capture all "neighbors"
309
+ # at the same (or greater) indentation and needs to expand out. For example
310
+ # the `def/end` lines surrounding a method.
181
311
  def scan_adjacent_indent
182
312
  before_after_indent = []
183
313
  before_after_indent << (next_up&.indent || 0)
@@ -189,6 +319,16 @@ module SyntaxSuggest
189
319
  self
190
320
  end
191
321
 
322
+ # TODO: Doc or delete
323
+ #
324
+ # I don't remember why this is needed, but it's called in code_context.
325
+ # It's related to the implementation of `capture_neighbor_context` somehow
326
+ # and that display improvement is only triggered when there's one visible line
327
+ #
328
+ # I think the primary purpose is to not include the current line in the
329
+ # logic evaluation of `capture_neighbor_context`. If that's true, then
330
+ # we should fix that method to handle this logic instead of only using
331
+ # it in one place and together.
192
332
  def start_at_next_line
193
333
  before_index
194
334
  after_index
@@ -197,26 +337,39 @@ module SyntaxSuggest
197
337
  self
198
338
  end
199
339
 
340
+ # Return the currently matched lines as a `CodeBlock`
341
+ #
342
+ # When a `CodeBlock` is created it will gather metadata about
343
+ # itself, so this is not a free conversion. Avoid allocating
344
+ # more CodeBlock's than needed
200
345
  def code_block
201
346
  CodeBlock.new(lines: lines)
202
347
  end
203
348
 
349
+ # Returns the lines matched by the current scan as an
350
+ # array of CodeLines
204
351
  def lines
205
352
  @code_lines[before_index..after_index]
206
353
  end
207
354
 
355
+ # Gives the index of the first line currently scanned
208
356
  def before_index
209
357
  @before_index ||= @orig_before_index
210
358
  end
211
359
 
360
+ # Gives the index of the last line currently scanned
212
361
  def after_index
213
362
  @after_index ||= @orig_after_index
214
363
  end
215
364
 
365
+ # Returns an array of all the CodeLines that exist before
366
+ # the currently scanned block
216
367
  private def before_lines
217
368
  @code_lines[0...before_index] || []
218
369
  end
219
370
 
371
+ # Returns an array of all the CodeLines that exist after
372
+ # the currently scanned block
220
373
  private def after_lines
221
374
  @code_lines[after_index.next..-1] || []
222
375
  end
@@ -35,30 +35,115 @@ module SyntaxSuggest
35
35
  @code_lines = code_lines
36
36
  end
37
37
 
38
+ # Main interface. Expand current indentation, before
39
+ # expanding to a lower indentation
38
40
  def call(block)
39
41
  if (next_block = expand_neighbors(block))
40
- return next_block
42
+ next_block
43
+ else
44
+ expand_indent(block)
41
45
  end
42
-
43
- expand_indent(block)
44
46
  end
45
47
 
48
+ # Expands code to the next lowest indentation
49
+ #
50
+ # For example:
51
+ #
52
+ # 1 def dog
53
+ # 2 print "dog"
54
+ # 3 end
55
+ #
56
+ # If a block starts on line 2 then it has captured all it's "neighbors" (code at
57
+ # the same indentation or higher). To continue expanding, this block must capture
58
+ # lines one and three which are at a different indentation level.
59
+ #
60
+ # This method allows fully expanded blocks to decrease their indentation level (so
61
+ # they can expand to capture more code up and down). It does this conservatively
62
+ # as there's no undo (currently).
46
63
  def expand_indent(block)
47
64
  AroundBlockScan.new(code_lines: @code_lines, block: block)
48
- .skip(:hidden?)
65
+ .force_add_hidden
49
66
  .stop_after_kw
50
67
  .scan_adjacent_indent
51
68
  .code_block
52
69
  end
53
70
 
71
+ # A neighbor is code that is at or above the current indent line.
72
+ #
73
+ # First we build a block with all neighbors. If we can't go further
74
+ # then we decrease the indentation threshold and expand via indentation
75
+ # i.e. `expand_indent`
76
+ #
77
+ # Handles two general cases.
78
+ #
79
+ # ## Case #1: Check code inside of methods/classes/etc.
80
+ #
81
+ # It's important to note, that not everything in a given indentation level can be parsed
82
+ # as valid code even if it's part of valid code. For example:
83
+ #
84
+ # 1 hash = {
85
+ # 2 name: "richard",
86
+ # 3 dog: "cinco",
87
+ # 4 }
88
+ #
89
+ # In this case lines 2 and 3 will be neighbors, but they're invalid until `expand_indent`
90
+ # is called on them.
91
+ #
92
+ # When we are adding code within a method or class (at the same indentation level),
93
+ # use the empty lines to denote the programmer intended logical chunks.
94
+ # Stop and check each one. For example:
95
+ #
96
+ # 1 def dog
97
+ # 2 print "dog"
98
+ # 3
99
+ # 4 hash = {
100
+ # 5 end
101
+ #
102
+ # If we did not stop parsing at empty newlines then the block might mistakenly grab all
103
+ # the contents (lines 2, 3, and 4) and report them as being problems, instead of only
104
+ # line 4.
105
+ #
106
+ # ## Case #2: Expand/grab other logical blocks
107
+ #
108
+ # Once the search algorithm has converted all lines into blocks at a given indentation
109
+ # it will then `expand_indent`. Once the blocks that generates are expanded as neighbors
110
+ # we then begin seeing neighbors being other logical blocks i.e. a block's neighbors
111
+ # may be another method or class (something with keywords/ends).
112
+ #
113
+ # For example:
114
+ #
115
+ # 1 def bark
116
+ # 2
117
+ # 3 end
118
+ # 4
119
+ # 5 def sit
120
+ # 6 end
121
+ #
122
+ # In this case if lines 4, 5, and 6 are in a block when it tries to expand neighbors
123
+ # it will expand up. If it stops after line 2 or 3 it may cause problems since there's a
124
+ # valid kw/end pair, but the block will be checked without it.
125
+ #
126
+ # We try to resolve this edge case with `lookahead_balance_one_line` below.
54
127
  def expand_neighbors(block)
55
- expanded_lines = AroundBlockScan.new(code_lines: @code_lines, block: block)
56
- .skip(:hidden?)
128
+ neighbors = AroundBlockScan.new(code_lines: @code_lines, block: block)
129
+ .force_add_hidden
57
130
  .stop_after_kw
58
- .scan_neighbors
59
- .scan_while { |line| line.empty? } # Slurp up empties
131
+ .scan_neighbors_not_empty
132
+
133
+ # Slurp up empties
134
+ with_empties = neighbors
135
+ .scan_while { |line| line.empty? }
136
+
137
+ # If next line is kw and it will balance us, take it
138
+ expanded_lines = with_empties
139
+ .lookahead_balance_one_line
60
140
  .lines
61
141
 
142
+ # Don't allocate a block if it won't be used
143
+ #
144
+ # If nothing was taken, return nil to indicate that status
145
+ # used in `def call` to determine if
146
+ # we need to expand up/out (`expand_indent`)
62
147
  if block.lines == expanded_lines
63
148
  nil
64
149
  else
@@ -76,7 +76,6 @@ module SyntaxSuggest
76
76
  # end
77
77
  # end
78
78
  #
79
- #
80
79
  def capture_falling_indent(block)
81
80
  AroundBlockScan.new(
82
81
  block: block,
@@ -110,7 +110,7 @@ module SyntaxSuggest
110
110
  @document.join
111
111
  end
112
112
 
113
- # Remove comments and whitespace only lines
113
+ # Remove comments
114
114
  #
115
115
  # replace with empty newlines
116
116
  #
@@ -155,8 +155,10 @@ module SyntaxSuggest
155
155
  # ).to eq(2)
156
156
  #
157
157
  def clean_sweep(source:)
158
+ # Match comments, but not HEREDOC strings with #{variable} interpolation
159
+ # https://rubular.com/r/HPwtW9OYxKUHXQ
158
160
  source.lines.map do |line|
159
- if line.match?(/^\s*(#[^{].*)?$/) # https://rubular.com/r/LLE10D8HKMkJvs
161
+ if line.match?(/^\s*#([^{].*|)$/)
160
162
  $/
161
163
  else
162
164
  line
@@ -48,12 +48,10 @@ module SyntaxSuggest
48
48
  strip_line = line.dup
49
49
  strip_line.lstrip!
50
50
 
51
- if strip_line.empty?
52
- @empty = true
53
- @indent = 0
51
+ @indent = if (@empty = strip_line.empty?)
52
+ line.length - 1 # Newline removed from strip_line is not "whitespace"
54
53
  else
55
- @empty = false
56
- @indent = line.length - strip_line.length
54
+ line.length - strip_line.length
57
55
  end
58
56
 
59
57
  set_kw_end
@@ -3,6 +3,10 @@
3
3
  # Ruby 3.2+ has a cleaner way to hook into Ruby that doesn't use `require`
4
4
  if SyntaxError.method_defined?(:detailed_message)
5
5
  module SyntaxSuggest
6
+ # Mini String IO [Private]
7
+ #
8
+ # Acts like a StringIO with reduced API, but without having to require that
9
+ # class.
6
10
  class MiniStringIO
7
11
  def initialize(isatty: $stderr.isatty)
8
12
  @string = +""
@@ -16,52 +20,59 @@ if SyntaxError.method_defined?(:detailed_message)
16
20
 
17
21
  attr_reader :string
18
22
  end
19
- end
20
-
21
- SyntaxError.prepend Module.new {
22
- def detailed_message(highlight: true, syntax_suggest: true, **kwargs)
23
- return super unless syntax_suggest
24
-
25
- require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
26
-
27
- message = super
28
- file = if highlight
29
- SyntaxSuggest::PathnameFromMessage.new(super(highlight: false, **kwargs)).call.name
30
- else
31
- SyntaxSuggest::PathnameFromMessage.new(message).call.name
32
- end
33
-
34
- io = SyntaxSuggest::MiniStringIO.new
35
23
 
36
- if file
37
- SyntaxSuggest.call(
38
- io: io,
39
- source: file.read,
40
- filename: file,
41
- terminal: highlight
42
- )
43
- annotation = io.string
44
-
45
- annotation + message
46
- else
47
- message
48
- end
49
- rescue => e
50
- if ENV["SYNTAX_SUGGEST_DEBUG"]
51
- $stderr.warn(e.message)
52
- $stderr.warn(e.backtrace)
53
- end
54
-
55
- # Ignore internal errors
56
- message
24
+ # SyntaxSuggest.record_dir [Private]
25
+ #
26
+ # Used to monkeypatch SyntaxError via Module.prepend
27
+ def self.module_for_detailed_message
28
+ Module.new {
29
+ def detailed_message(highlight: true, syntax_suggest: true, **kwargs)
30
+ return super unless syntax_suggest
31
+
32
+ require "syntax_suggest/api" unless defined?(SyntaxSuggest::DEFAULT_VALUE)
33
+
34
+ message = super
35
+
36
+ if path
37
+ file = Pathname.new(path)
38
+ io = SyntaxSuggest::MiniStringIO.new
39
+
40
+ SyntaxSuggest.call(
41
+ io: io,
42
+ source: file.read,
43
+ filename: file,
44
+ terminal: highlight
45
+ )
46
+ annotation = io.string
47
+
48
+ annotation + message
49
+ else
50
+ message
51
+ end
52
+ rescue => e
53
+ if ENV["SYNTAX_SUGGEST_DEBUG"]
54
+ $stderr.warn(e.message)
55
+ $stderr.warn(e.backtrace)
56
+ end
57
+
58
+ # Ignore internal errors
59
+ message
60
+ end
61
+ }
57
62
  end
58
- }
63
+ end
64
+
65
+ SyntaxError.prepend(SyntaxSuggest.module_for_detailed_message)
59
66
  else
60
67
  autoload :Pathname, "pathname"
61
68
 
69
+ #--
62
70
  # Monkey patch kernel to ensure that all `require` calls call the same
63
71
  # method
72
+ #++
64
73
  module Kernel
74
+ # :stopdoc:
75
+
65
76
  module_function
66
77
 
67
78
  alias_method :syntax_suggest_original_require, :require
@@ -36,8 +36,8 @@ module SyntaxSuggest
36
36
  # Builds blocks from bottom up
37
37
  def each_neighbor_block(target_line)
38
38
  scan = AroundBlockScan.new(code_lines: code_lines, block: CodeBlock.new(lines: target_line))
39
- .skip(:empty?)
40
- .skip(:hidden?)
39
+ .force_add_empty
40
+ .force_add_hidden
41
41
  .scan_while { |line| line.indent >= target_line.indent }
42
42
 
43
43
  neighbors = scan.code_block.lines
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxSuggest
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntax_suggest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - schneems
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-11-29 00:00:00.000000000 Z
11
+ date: 2023-03-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: When you get an "unexpected end" in your syntax this gem helps you find
14
14
  it
@@ -19,6 +19,7 @@ executables:
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
+ - ".github/dependabot.yml"
22
23
  - ".github/workflows/check_changelog.yml"
23
24
  - ".github/workflows/ci.yml"
24
25
  - ".gitignore"
@@ -32,6 +33,8 @@ files:
32
33
  - README.md
33
34
  - Rakefile
34
35
  - bin/console
36
+ - bin/rake
37
+ - bin/rspec
35
38
  - bin/setup
36
39
  - exe/syntax_suggest
37
40
  - lib/syntax_suggest.rb
@@ -81,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
84
  - !ruby/object:Gem::Version
82
85
  version: '0'
83
86
  requirements: []
84
- rubygems_version: 3.3.7
87
+ rubygems_version: 3.4.6
85
88
  signing_key:
86
89
  specification_version: 4
87
90
  summary: Find syntax errors in your source in a snap