syntax_suggest 1.0.4 → 2.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.
- checksums.yaml +4 -4
- data/.github/workflows/check_changelog.yml +1 -1
- data/.github/workflows/ci.yml +31 -11
- data/.standard.yml +1 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +53 -35
- data/README.md +2 -2
- data/lib/syntax_suggest/api.rb +42 -7
- data/lib/syntax_suggest/around_block_scan.rb +68 -213
- data/lib/syntax_suggest/block_expand.rb +11 -5
- data/lib/syntax_suggest/capture/before_after_keyword_ends.rb +85 -0
- data/lib/syntax_suggest/capture/falling_indent_lines.rb +71 -0
- data/lib/syntax_suggest/capture_code_context.rb +18 -5
- data/lib/syntax_suggest/clean_document.rb +6 -6
- data/lib/syntax_suggest/code_block.rb +1 -1
- data/lib/syntax_suggest/code_frontier.rb +1 -1
- data/lib/syntax_suggest/code_line.rb +12 -5
- data/lib/syntax_suggest/code_search.rb +2 -2
- data/lib/syntax_suggest/core_ext.rb +1 -1
- data/lib/syntax_suggest/display_invalid_blocks.rb +1 -1
- data/lib/syntax_suggest/explain_syntax.rb +18 -4
- data/lib/syntax_suggest/lex_all.rb +29 -10
- data/lib/syntax_suggest/pathname_from_message.rb +1 -1
- data/lib/syntax_suggest/ripper_errors.rb +4 -1
- data/lib/syntax_suggest/scan_history.rb +134 -0
- data/lib/syntax_suggest/version.rb +1 -1
- data/syntax_suggest.gemspec +2 -2
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b3d83011e29784a7ea97dcae359b654ec8be6382c9fda7a5114353050ef5bf2
|
4
|
+
data.tar.gz: a2452866035aa4751948ac4f30cb7699352bc0778fb4b5bcae3ce3f421b901de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52168116c672ee83619f59d1f158101adb64c650770424c3bd855fafb71131f72663c8afd060f8512bbe908fae2ecb8dea5a65c9384b980843c89901f70e202a
|
7
|
+
data.tar.gz: 5edfacbb66e7b37dc36f1b57de325ffea4656102b3a4638249577fb8c12460c2e05a3f7761675966e845214db891c83f5b51eef963d35da506478af7f0837874
|
@@ -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@
|
16
|
+
- uses: actions/checkout@v4.1.1
|
17
17
|
- name: Check that CHANGELOG is touched
|
18
18
|
run: |
|
19
19
|
git fetch origin ${{ github.base_ref }} --depth 1 && \
|
data/.github/workflows/ci.yml
CHANGED
@@ -9,31 +9,32 @@ jobs:
|
|
9
9
|
runs-on: ubuntu-latest
|
10
10
|
steps:
|
11
11
|
- name: Checkout code
|
12
|
-
uses: actions/checkout@
|
12
|
+
uses: actions/checkout@v4.1.1
|
13
13
|
- name: Set up Ruby
|
14
14
|
uses: ruby/setup-ruby@v1
|
15
15
|
with:
|
16
|
-
ruby-version:
|
16
|
+
ruby-version: ruby
|
17
17
|
bundler-cache: true
|
18
18
|
- name: Linting
|
19
19
|
run: bundle exec standardrb
|
20
|
+
env:
|
21
|
+
RUBYOPT: --disable=syntax_suggest
|
22
|
+
|
23
|
+
ruby-versions:
|
24
|
+
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
|
25
|
+
with:
|
26
|
+
engine: cruby
|
20
27
|
|
21
28
|
test:
|
29
|
+
needs: ruby-versions
|
22
30
|
runs-on: ubuntu-latest
|
23
31
|
strategy:
|
24
32
|
fail-fast: false
|
25
33
|
matrix:
|
26
|
-
ruby:
|
27
|
-
- 2.5
|
28
|
-
- 2.6
|
29
|
-
- 2.7
|
30
|
-
- '3.0'
|
31
|
-
- 3.1
|
32
|
-
- 3.2
|
33
|
-
- head
|
34
|
+
ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
|
34
35
|
steps:
|
35
36
|
- name: Checkout code
|
36
|
-
uses: actions/checkout@
|
37
|
+
uses: actions/checkout@v4.1.1
|
37
38
|
- name: Set up Ruby
|
38
39
|
uses: ruby/setup-ruby@v1
|
39
40
|
with:
|
@@ -42,3 +43,22 @@ jobs:
|
|
42
43
|
- name: test
|
43
44
|
run: bin/rake test
|
44
45
|
continue-on-error: ${{ matrix.ruby == 'head' }}
|
46
|
+
|
47
|
+
test-disable-prism:
|
48
|
+
needs: ruby-versions
|
49
|
+
runs-on: ubuntu-latest
|
50
|
+
strategy:
|
51
|
+
fail-fast: false
|
52
|
+
matrix:
|
53
|
+
ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
|
54
|
+
steps:
|
55
|
+
- name: Checkout code
|
56
|
+
uses: actions/checkout@v4.1.1
|
57
|
+
- name: Set up Ruby
|
58
|
+
uses: ruby/setup-ruby@v1
|
59
|
+
with:
|
60
|
+
ruby-version: ${{ matrix.ruby }}
|
61
|
+
bundler-cache: true
|
62
|
+
- name: test
|
63
|
+
run: SYNTAX_SUGGEST_DISABLE_PRISM=1 bin/rake test
|
64
|
+
continue-on-error: ${{ matrix.ruby == 'head' }}
|
data/.standard.yml
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby_version:
|
1
|
+
ruby_version: 3.0.0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
## HEAD (unreleased)
|
2
2
|
|
3
|
+
## 2.0.0
|
4
|
+
|
5
|
+
- Changed: No longer supports EOL versions of Ruby. (https://github.com/ruby/syntax_suggest/pull/210)
|
6
|
+
- Added: Support prism parser (https://github.com/ruby/syntax_suggest/pull/208).
|
7
|
+
- Added: Handle Ruby 3.3 new eval source location format (https://github.com/ruby/syntax_suggest/pull/200).
|
8
|
+
|
9
|
+
## 1.1.0
|
10
|
+
|
11
|
+
- Handle if/else with comment or empty line in branch (https://github.com/ruby/syntax_suggest/pull/193)
|
12
|
+
- Use `SYNTAX_SUGGEST_DEBUG` instead of `DEBUG` env var value in timeout warning message (https://github.com/ruby/syntax_suggest/pull/194)
|
13
|
+
- Reduce line output for increased clarity (https://github.com/ruby/syntax_suggest/pull/190)
|
14
|
+
|
3
15
|
## 1.0.4
|
4
16
|
|
5
17
|
- Fix rendering a file without a newline ending (https://github.com/ruby/syntax_suggest/pull/182)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,61 +1,79 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_suggest (
|
4
|
+
syntax_suggest (2.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ast (2.4.2)
|
10
|
-
benchmark-ips (2.
|
11
|
-
diff-lcs (1.
|
12
|
-
|
13
|
-
|
10
|
+
benchmark-ips (2.12.0)
|
11
|
+
diff-lcs (1.5.0)
|
12
|
+
json (2.7.0)
|
13
|
+
language_server-protocol (3.17.0.3)
|
14
|
+
lint_roller (1.1.0)
|
15
|
+
parallel (1.23.0)
|
16
|
+
parser (3.2.2.4)
|
14
17
|
ast (~> 2.4.1)
|
15
|
-
|
18
|
+
racc
|
19
|
+
prism (0.18.0)
|
20
|
+
racc (1.7.3)
|
21
|
+
rainbow (3.1.1)
|
16
22
|
rake (12.3.3)
|
17
|
-
regexp_parser (2.
|
18
|
-
rexml (3.2.
|
19
|
-
rspec (3.
|
20
|
-
rspec-core (~> 3.
|
21
|
-
rspec-expectations (~> 3.
|
22
|
-
rspec-mocks (~> 3.
|
23
|
-
rspec-core (3.
|
24
|
-
rspec-support (~> 3.
|
25
|
-
rspec-expectations (3.
|
23
|
+
regexp_parser (2.8.3)
|
24
|
+
rexml (3.2.6)
|
25
|
+
rspec (3.12.0)
|
26
|
+
rspec-core (~> 3.12.0)
|
27
|
+
rspec-expectations (~> 3.12.0)
|
28
|
+
rspec-mocks (~> 3.12.0)
|
29
|
+
rspec-core (3.12.2)
|
30
|
+
rspec-support (~> 3.12.0)
|
31
|
+
rspec-expectations (3.12.3)
|
26
32
|
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
-
rspec-support (~> 3.
|
28
|
-
rspec-mocks (3.
|
33
|
+
rspec-support (~> 3.12.0)
|
34
|
+
rspec-mocks (3.12.6)
|
29
35
|
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
-
rspec-support (~> 3.
|
31
|
-
rspec-support (3.
|
32
|
-
rubocop (1.
|
36
|
+
rspec-support (~> 3.12.0)
|
37
|
+
rspec-support (3.12.1)
|
38
|
+
rubocop (1.57.2)
|
39
|
+
json (~> 2.3)
|
40
|
+
language_server-protocol (>= 3.17.0)
|
33
41
|
parallel (~> 1.10)
|
34
|
-
parser (>= 3.
|
42
|
+
parser (>= 3.2.2.4)
|
35
43
|
rainbow (>= 2.2.2, < 4.0)
|
36
44
|
regexp_parser (>= 1.8, < 3.0)
|
37
|
-
rexml
|
38
|
-
rubocop-ast (>= 1.
|
45
|
+
rexml (>= 3.2.5, < 4.0)
|
46
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
39
47
|
ruby-progressbar (~> 1.7)
|
40
|
-
unicode-display_width (>=
|
41
|
-
rubocop-ast (1.
|
42
|
-
parser (>= 3.
|
43
|
-
rubocop-performance (1.
|
48
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
49
|
+
rubocop-ast (1.30.0)
|
50
|
+
parser (>= 3.2.1.0)
|
51
|
+
rubocop-performance (1.19.1)
|
44
52
|
rubocop (>= 1.7.0, < 2.0)
|
45
53
|
rubocop-ast (>= 0.4.0)
|
46
|
-
ruby-prof (1.
|
47
|
-
ruby-progressbar (1.
|
48
|
-
stackprof (0.2.
|
49
|
-
standard (1.
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
ruby-prof (1.6.3)
|
55
|
+
ruby-progressbar (1.13.0)
|
56
|
+
stackprof (0.2.25)
|
57
|
+
standard (1.32.1)
|
58
|
+
language_server-protocol (~> 3.17.0.2)
|
59
|
+
lint_roller (~> 1.0)
|
60
|
+
rubocop (~> 1.57.2)
|
61
|
+
standard-custom (~> 1.0.0)
|
62
|
+
standard-performance (~> 1.2)
|
63
|
+
standard-custom (1.0.2)
|
64
|
+
lint_roller (~> 1.0)
|
65
|
+
rubocop (~> 1.50)
|
66
|
+
standard-performance (1.2.1)
|
67
|
+
lint_roller (~> 1.1)
|
68
|
+
rubocop-performance (~> 1.19.1)
|
69
|
+
unicode-display_width (2.5.0)
|
53
70
|
|
54
71
|
PLATFORMS
|
55
72
|
ruby
|
56
73
|
|
57
74
|
DEPENDENCIES
|
58
75
|
benchmark-ips
|
76
|
+
prism
|
59
77
|
rake (~> 12.0)
|
60
78
|
rspec (~> 3.0)
|
61
79
|
ruby-prof
|
@@ -64,4 +82,4 @@ DEPENDENCIES
|
|
64
82
|
syntax_suggest!
|
65
83
|
|
66
84
|
BUNDLED WITH
|
67
|
-
2.
|
85
|
+
2.4.21
|
data/README.md
CHANGED
@@ -122,7 +122,7 @@ Unmatched `(', missing `)' ?
|
|
122
122
|
5 end
|
123
123
|
```
|
124
124
|
|
125
|
-
- Any ambiguous or unknown errors will be annotated by the original
|
125
|
+
- Any ambiguous or unknown errors will be annotated by the original parser error output:
|
126
126
|
|
127
127
|
<!--
|
128
128
|
class Dog
|
@@ -133,7 +133,7 @@ end
|
|
133
133
|
-->
|
134
134
|
|
135
135
|
```
|
136
|
-
|
136
|
+
Expected an expression after the operator
|
137
137
|
|
138
138
|
1 class Dog
|
139
139
|
2 def meals_last_month
|
data/lib/syntax_suggest/api.rb
CHANGED
@@ -5,9 +5,28 @@ require_relative "version"
|
|
5
5
|
require "tmpdir"
|
6
6
|
require "stringio"
|
7
7
|
require "pathname"
|
8
|
-
require "ripper"
|
9
8
|
require "timeout"
|
10
9
|
|
10
|
+
# We need Ripper loaded for `Prism.lex_compat` even if we're using Prism
|
11
|
+
# for lexing and parsing
|
12
|
+
require "ripper"
|
13
|
+
|
14
|
+
# Prism is the new parser, replacing Ripper
|
15
|
+
#
|
16
|
+
# We need to "dual boot" both for now because syntax_suggest
|
17
|
+
# supports older rubies that do not ship with syntax suggest.
|
18
|
+
#
|
19
|
+
# We also need the ability to control loading of this library
|
20
|
+
# so we can test that both modes work correctly in CI.
|
21
|
+
if (value = ENV["SYNTAX_SUGGEST_DISABLE_PRISM"])
|
22
|
+
warn "Skipping loading prism due to SYNTAX_SUGGEST_DISABLE_PRISM=#{value}"
|
23
|
+
else
|
24
|
+
begin
|
25
|
+
require "prism"
|
26
|
+
rescue LoadError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
11
30
|
module SyntaxSuggest
|
12
31
|
# Used to indicate a default value that cannot
|
13
32
|
# be confused with another input.
|
@@ -16,6 +35,14 @@ module SyntaxSuggest
|
|
16
35
|
class Error < StandardError; end
|
17
36
|
TIMEOUT_DEFAULT = ENV.fetch("SYNTAX_SUGGEST_TIMEOUT", 1).to_i
|
18
37
|
|
38
|
+
# SyntaxSuggest.use_prism_parser? [Private]
|
39
|
+
#
|
40
|
+
# Tells us if the prism parser is available for use
|
41
|
+
# or if we should fallback to `Ripper`
|
42
|
+
def self.use_prism_parser?
|
43
|
+
defined?(Prism)
|
44
|
+
end
|
45
|
+
|
19
46
|
# SyntaxSuggest.handle_error [Public]
|
20
47
|
#
|
21
48
|
# Takes a `SyntaxError` exception, uses the
|
@@ -78,7 +105,7 @@ module SyntaxSuggest
|
|
78
105
|
code_lines: search.code_lines
|
79
106
|
).call
|
80
107
|
rescue Timeout::Error => e
|
81
|
-
io.puts "Search timed out SYNTAX_SUGGEST_TIMEOUT=#{timeout}, run with
|
108
|
+
io.puts "Search timed out SYNTAX_SUGGEST_TIMEOUT=#{timeout}, run with SYNTAX_SUGGEST_DEBUG=1 for more info"
|
82
109
|
io.puts e.backtrace.first(3).join($/)
|
83
110
|
end
|
84
111
|
|
@@ -129,11 +156,20 @@ module SyntaxSuggest
|
|
129
156
|
# SyntaxSuggest.invalid? [Private]
|
130
157
|
#
|
131
158
|
# Opposite of `SyntaxSuggest.valid?`
|
132
|
-
|
133
|
-
|
134
|
-
|
159
|
+
if defined?(Prism)
|
160
|
+
def self.invalid?(source)
|
161
|
+
source = source.join if source.is_a?(Array)
|
162
|
+
source = source.to_s
|
135
163
|
|
136
|
-
|
164
|
+
Prism.parse(source).failure?
|
165
|
+
end
|
166
|
+
else
|
167
|
+
def self.invalid?(source)
|
168
|
+
source = source.join if source.is_a?(Array)
|
169
|
+
source = source.to_s
|
170
|
+
|
171
|
+
Ripper.new(source).tap(&:parse).error?
|
172
|
+
end
|
137
173
|
end
|
138
174
|
|
139
175
|
# SyntaxSuggest.valid? [Private]
|
@@ -191,7 +227,6 @@ require_relative "lex_all"
|
|
191
227
|
require_relative "code_line"
|
192
228
|
require_relative "code_block"
|
193
229
|
require_relative "block_expand"
|
194
|
-
require_relative "ripper_errors"
|
195
230
|
require_relative "priority_queue"
|
196
231
|
require_relative "unvisited_lines"
|
197
232
|
require_relative "around_block_scan"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "scan_history"
|
4
|
+
|
3
5
|
module SyntaxSuggest
|
4
6
|
# This class is useful for exploring contents before and after
|
5
7
|
# a block
|
@@ -24,22 +26,17 @@ module SyntaxSuggest
|
|
24
26
|
# puts scan.before_index # => 0
|
25
27
|
# puts scan.after_index # => 3
|
26
28
|
#
|
27
|
-
# Contents can also be filtered using AroundBlockScan#skip
|
28
|
-
#
|
29
|
-
# To grab the next surrounding indentation use AroundBlockScan#scan_adjacent_indent
|
30
29
|
class AroundBlockScan
|
31
30
|
def initialize(code_lines:, block:)
|
32
31
|
@code_lines = code_lines
|
33
|
-
@orig_before_index = block.lines.first.index
|
34
|
-
@orig_after_index = block.lines.last.index
|
35
32
|
@orig_indent = block.current_indent
|
36
|
-
@skip_array = []
|
37
|
-
@after_array = []
|
38
|
-
@before_array = []
|
39
|
-
@stop_after_kw = false
|
40
33
|
|
41
|
-
@
|
34
|
+
@stop_after_kw = false
|
42
35
|
@force_add_empty = false
|
36
|
+
@force_add_hidden = false
|
37
|
+
@target_indent = nil
|
38
|
+
|
39
|
+
@scanner = ScanHistory.new(code_lines: code_lines, block: block)
|
43
40
|
end
|
44
41
|
|
45
42
|
# When using this flag, `scan_while` will
|
@@ -89,150 +86,35 @@ module SyntaxSuggest
|
|
89
86
|
# stopping if we've found a keyword/end mis-match in one direction
|
90
87
|
# or the other.
|
91
88
|
def scan_while
|
92
|
-
|
93
|
-
|
94
|
-
end_count = 0
|
95
|
-
index = before_lines.reverse_each.take_while do |line|
|
96
|
-
next false if stop_next
|
97
|
-
next true if @force_add_hidden && line.hidden?
|
98
|
-
next true if @force_add_empty && line.empty?
|
99
|
-
|
100
|
-
kw_count += 1 if line.is_kw?
|
101
|
-
end_count += 1 if line.is_end?
|
102
|
-
if @stop_after_kw && kw_count > end_count
|
103
|
-
stop_next = true
|
104
|
-
end
|
105
|
-
|
106
|
-
yield line
|
107
|
-
end.last&.index
|
108
|
-
|
109
|
-
if index && index < before_index
|
110
|
-
@before_index = index
|
111
|
-
end
|
112
|
-
|
113
|
-
stop_next = false
|
114
|
-
kw_count = 0
|
115
|
-
end_count = 0
|
116
|
-
index = after_lines.take_while do |line|
|
117
|
-
next false if stop_next
|
118
|
-
next true if @force_add_hidden && line.hidden?
|
119
|
-
next true if @force_add_empty && line.empty?
|
120
|
-
|
121
|
-
kw_count += 1 if line.is_kw?
|
122
|
-
end_count += 1 if line.is_end?
|
123
|
-
if @stop_after_kw && end_count > kw_count
|
124
|
-
stop_next = true
|
125
|
-
end
|
126
|
-
|
127
|
-
yield line
|
128
|
-
end.last&.index
|
129
|
-
|
130
|
-
if index && index > after_index
|
131
|
-
@after_index = index
|
132
|
-
end
|
133
|
-
self
|
134
|
-
end
|
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
|
-
#
|
163
|
-
def capture_neighbor_context
|
164
|
-
lines = []
|
165
|
-
kw_count = 0
|
166
|
-
end_count = 0
|
167
|
-
before_lines.reverse_each do |line|
|
168
|
-
next if line.empty?
|
169
|
-
break if line.indent < @orig_indent
|
170
|
-
next if line.indent != @orig_indent
|
171
|
-
|
172
|
-
kw_count += 1 if line.is_kw?
|
173
|
-
end_count += 1 if line.is_end?
|
174
|
-
if kw_count != 0 && kw_count == end_count
|
175
|
-
lines << line
|
176
|
-
break
|
177
|
-
end
|
178
|
-
|
179
|
-
lines << line
|
180
|
-
end
|
89
|
+
stop_next_up = false
|
90
|
+
stop_next_down = false
|
181
91
|
|
182
|
-
|
92
|
+
@scanner.scan(
|
93
|
+
up: ->(line, kw_count, end_count) {
|
94
|
+
next false if stop_next_up
|
95
|
+
next true if @force_add_hidden && line.hidden?
|
96
|
+
next true if @force_add_empty && line.empty?
|
183
97
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
next if line.empty?
|
188
|
-
break if line.indent < @orig_indent
|
189
|
-
next if line.indent != @orig_indent
|
98
|
+
if @stop_after_kw && kw_count > end_count
|
99
|
+
stop_next_up = true
|
100
|
+
end
|
190
101
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
lines << line
|
199
|
-
end
|
102
|
+
yield line
|
103
|
+
},
|
104
|
+
down: ->(line, kw_count, end_count) {
|
105
|
+
next false if stop_next_down
|
106
|
+
next true if @force_add_hidden && line.hidden?
|
107
|
+
next true if @force_add_empty && line.empty?
|
200
108
|
|
201
|
-
|
202
|
-
|
109
|
+
if @stop_after_kw && end_count > kw_count
|
110
|
+
stop_next_down = true
|
111
|
+
end
|
203
112
|
|
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
|
-
#
|
218
|
-
def on_falling_indent
|
219
|
-
last_indent = @orig_indent
|
220
|
-
before_lines.reverse_each do |line|
|
221
|
-
next if line.empty?
|
222
|
-
if line.indent < last_indent
|
223
113
|
yield line
|
224
|
-
|
225
|
-
|
226
|
-
end
|
114
|
+
}
|
115
|
+
)
|
227
116
|
|
228
|
-
|
229
|
-
after_lines.each do |line|
|
230
|
-
next if line.empty?
|
231
|
-
if line.indent < last_indent
|
232
|
-
yield line
|
233
|
-
last_indent = line.indent
|
234
|
-
end
|
235
|
-
end
|
117
|
+
self
|
236
118
|
end
|
237
119
|
|
238
120
|
# Scanning is intentionally conservative because
|
@@ -266,39 +148,46 @@ module SyntaxSuggest
|
|
266
148
|
|
267
149
|
return self if kw_count == end_count # nothing to balance
|
268
150
|
|
269
|
-
#
|
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
|
151
|
+
@scanner.commit_if_changed # Rollback point if we don't find anything to optimize
|
275
152
|
|
276
|
-
#
|
277
|
-
|
278
|
-
|
279
|
-
|
153
|
+
# Try to eat up empty lines
|
154
|
+
@scanner.scan(
|
155
|
+
up: ->(line, _, _) { line.hidden? || line.empty? },
|
156
|
+
down: ->(line, _, _) { line.hidden? || line.empty? }
|
157
|
+
)
|
280
158
|
|
281
|
-
|
159
|
+
# More ends than keywords, check if we can balance expanding up
|
160
|
+
next_up = @scanner.next_up
|
161
|
+
next_down = @scanner.next_down
|
162
|
+
case end_count - kw_count
|
163
|
+
when 1
|
164
|
+
if next_up&.is_kw? && next_up.indent >= @target_indent
|
165
|
+
@scanner.scan(
|
166
|
+
up: ->(line, _, _) { line == next_up },
|
167
|
+
down: ->(line, _, _) { false }
|
168
|
+
)
|
169
|
+
@scanner.commit_if_changed
|
170
|
+
end
|
171
|
+
when -1
|
172
|
+
if next_down&.is_end? && next_down.indent >= @target_indent
|
173
|
+
@scanner.scan(
|
174
|
+
up: ->(line, _, _) { false },
|
175
|
+
down: ->(line, _, _) { line == next_down }
|
176
|
+
)
|
177
|
+
@scanner.commit_if_changed
|
178
|
+
end
|
282
179
|
end
|
180
|
+
# Rollback any uncommitted changes
|
181
|
+
@scanner.stash_changes
|
182
|
+
|
283
183
|
self
|
284
184
|
end
|
285
185
|
|
286
186
|
# Finds code lines at the same or greater indentation and adds them
|
287
187
|
# to the block
|
288
188
|
def scan_neighbors_not_empty
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
# Returns the next line to be scanned above the current block.
|
293
|
-
# Returns `nil` if at the top of the document already
|
294
|
-
def next_up
|
295
|
-
@code_lines[before_index.pred]
|
296
|
-
end
|
297
|
-
|
298
|
-
# Returns the next line to be scanned below the current block.
|
299
|
-
# Returns `nil` if at the bottom of the document already
|
300
|
-
def next_down
|
301
|
-
@code_lines[after_index.next]
|
189
|
+
@target_indent = @orig_indent
|
190
|
+
scan_while { |line| line.not_empty? && line.indent >= @target_indent }
|
302
191
|
end
|
303
192
|
|
304
193
|
# Scan blocks based on indentation of next line above/below block
|
@@ -310,30 +199,13 @@ module SyntaxSuggest
|
|
310
199
|
# the `def/end` lines surrounding a method.
|
311
200
|
def scan_adjacent_indent
|
312
201
|
before_after_indent = []
|
313
|
-
before_after_indent << (next_up&.indent || 0)
|
314
|
-
before_after_indent << (next_down&.indent || 0)
|
315
202
|
|
316
|
-
indent
|
317
|
-
|
203
|
+
before_after_indent << (@scanner.next_up&.indent || 0)
|
204
|
+
before_after_indent << (@scanner.next_down&.indent || 0)
|
318
205
|
|
319
|
-
|
320
|
-
|
206
|
+
@target_indent = before_after_indent.min
|
207
|
+
scan_while { |line| line.not_empty? && line.indent >= @target_indent }
|
321
208
|
|
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.
|
332
|
-
def start_at_next_line
|
333
|
-
before_index
|
334
|
-
after_index
|
335
|
-
@before_index -= 1
|
336
|
-
@after_index += 1
|
337
209
|
self
|
338
210
|
end
|
339
211
|
|
@@ -349,29 +221,12 @@ module SyntaxSuggest
|
|
349
221
|
# Returns the lines matched by the current scan as an
|
350
222
|
# array of CodeLines
|
351
223
|
def lines
|
352
|
-
@
|
353
|
-
end
|
354
|
-
|
355
|
-
# Gives the index of the first line currently scanned
|
356
|
-
def before_index
|
357
|
-
@before_index ||= @orig_before_index
|
358
|
-
end
|
359
|
-
|
360
|
-
# Gives the index of the last line currently scanned
|
361
|
-
def after_index
|
362
|
-
@after_index ||= @orig_after_index
|
363
|
-
end
|
364
|
-
|
365
|
-
# Returns an array of all the CodeLines that exist before
|
366
|
-
# the currently scanned block
|
367
|
-
private def before_lines
|
368
|
-
@code_lines[0...before_index] || []
|
224
|
+
@scanner.lines
|
369
225
|
end
|
370
226
|
|
371
|
-
#
|
372
|
-
|
373
|
-
|
374
|
-
@code_lines[after_index.next..-1] || []
|
227
|
+
# Managable rspec errors
|
228
|
+
def inspect
|
229
|
+
"#<#{self.class}:0x0000123843lol >"
|
375
230
|
end
|
376
231
|
end
|
377
232
|
end
|