syntax_tree 2.8.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -1
- data/Gemfile.lock +6 -4
- data/README.md +3 -3
- data/lib/syntax_tree/cli.rb +25 -25
- data/lib/syntax_tree/formatter.rb +3 -1
- data/lib/syntax_tree/language_server/inlay_hints.rb +42 -18
- data/lib/syntax_tree/language_server.rb +25 -39
- data/lib/syntax_tree/parser.rb +6 -6
- data/lib/syntax_tree/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 499aba04c732c3101f3e3ebb7d487a19842781a0a97b67fad047c2cf9bbe2bcb
|
4
|
+
data.tar.gz: dc9e28898951ae74f3a053b261b8d1e752567917bbb8eb34a4aa22af6b49b716
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a8cdd3933efaaa8898cfa25bebd3e52d3b6632081319d8ddded59dacb996ebd11232d1060e408eb93bdb4211aa4d2775bfce970ba868264f7846dcb63873e5e
|
7
|
+
data.tar.gz: 673c601ce6a25cc4875b2041710b4a85f5114a24deb1cb5e3bf69a464e072f72f24f2e13cb3aa89d95076a5d7643beb6469ccd669018e75d9ac998aeb0a10ddf
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,28 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [3.0.1] - 2022-07-15
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- [#112](https://github.com/ruby-syntax-tree/syntax_tree/pull/112) - Fix parallel CLI execution by not short-circuiting with the `||` operator.
|
14
|
+
|
15
|
+
## [3.0.0] - 2022-07-04
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
- [#102](https://github.com/ruby-syntax-tree/syntax_tree/issues/102) - Handle requests to the language server for files that do not yet exist on disk.
|
20
|
+
|
21
|
+
### Removed
|
22
|
+
|
23
|
+
- [#108](https://github.com/ruby-syntax-tree/syntax_tree/pull/108) - Remove old inlay hints code.
|
24
|
+
|
25
|
+
## [2.9.0] - 2022-07-04
|
26
|
+
|
27
|
+
### Added
|
28
|
+
|
29
|
+
- [#106](https://github.com/ruby-syntax-tree/syntax_tree/pull/106) - Add inlay hint support to match the LSP specification.
|
30
|
+
|
9
31
|
## [2.8.0] - 2022-06-21
|
10
32
|
|
11
33
|
### Added
|
@@ -272,7 +294,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
272
294
|
|
273
295
|
- 🎉 Initial release! 🎉
|
274
296
|
|
275
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/
|
297
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.0.1...HEAD
|
298
|
+
[3.0.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v3.0.0...v3.0.1
|
299
|
+
[3.0.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.9.0...v3.0.0
|
300
|
+
[2.9.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.8.0...v2.9.0
|
276
301
|
[2.8.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.1...v2.8.0
|
277
302
|
[2.7.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.0...v2.7.1
|
278
303
|
[2.7.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.6.0...v2.7.0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (
|
4
|
+
syntax_tree (3.0.1)
|
5
5
|
prettier_print
|
6
6
|
|
7
7
|
GEM
|
@@ -9,7 +9,8 @@ GEM
|
|
9
9
|
specs:
|
10
10
|
ast (2.4.2)
|
11
11
|
docile (1.4.0)
|
12
|
-
|
12
|
+
json (2.6.2)
|
13
|
+
minitest (5.16.2)
|
13
14
|
parallel (1.22.1)
|
14
15
|
parser (3.1.2.0)
|
15
16
|
ast (~> 2.4.1)
|
@@ -18,7 +19,8 @@ GEM
|
|
18
19
|
rake (13.0.6)
|
19
20
|
regexp_parser (2.5.0)
|
20
21
|
rexml (3.2.5)
|
21
|
-
rubocop (1.
|
22
|
+
rubocop (1.31.2)
|
23
|
+
json (~> 2.3)
|
22
24
|
parallel (~> 1.10)
|
23
25
|
parser (>= 3.1.0.0)
|
24
26
|
rainbow (>= 2.2.2, < 4.0)
|
@@ -36,7 +38,7 @@ GEM
|
|
36
38
|
simplecov_json_formatter (~> 0.1)
|
37
39
|
simplecov-html (0.12.3)
|
38
40
|
simplecov_json_formatter (0.1.4)
|
39
|
-
unicode-display_width (2.
|
41
|
+
unicode-display_width (2.2.0)
|
40
42
|
|
41
43
|
PLATFORMS
|
42
44
|
arm64-darwin-21
|
data/README.md
CHANGED
@@ -35,7 +35,7 @@ It is built with only standard library dependencies. It additionally ships with
|
|
35
35
|
- [BasicVisitor](#basicvisitor)
|
36
36
|
- [Language server](#language-server)
|
37
37
|
- [textDocument/formatting](#textdocumentformatting)
|
38
|
-
- [textDocument/
|
38
|
+
- [textDocument/inlayHint](#textdocumentinlayhint)
|
39
39
|
- [syntaxTree/visualizing](#syntaxtreevisualizing)
|
40
40
|
- [Plugins](#plugins)
|
41
41
|
- [Configuration](#configuration)
|
@@ -402,7 +402,7 @@ By default, the language server is relatively minimal, mostly meant to provide a
|
|
402
402
|
|
403
403
|
As mentioned above, the language server responds to formatting requests with the formatted document. It typically responds on the order of tens of milliseconds, so it should be fast enough for any IDE.
|
404
404
|
|
405
|
-
### textDocument/
|
405
|
+
### textDocument/inlayHint
|
406
406
|
|
407
407
|
The language server also responds to the relatively new inlay hints request. This request allows the language server to define additional information that should exist in the source code as helpful hints to the developer. In our case we use it to display things like implicit parentheses. For example, if you had the following code:
|
408
408
|
|
@@ -410,7 +410,7 @@ The language server also responds to the relatively new inlay hints request. Thi
|
|
410
410
|
1 + 2 * 3
|
411
411
|
```
|
412
412
|
|
413
|
-
Implicity, the `2 * 3` is going to be executed first because the `*` operator has higher precedence than the `+` operator.
|
413
|
+
Implicity, the `2 * 3` is going to be executed first because the `*` operator has higher precedence than the `+` operator. To ease mental overhead, our language server includes small parentheses to make this explicit, as in:
|
414
414
|
|
415
415
|
```ruby
|
416
416
|
1 + ₍2 * 3₎
|
data/lib/syntax_tree/cli.rb
CHANGED
@@ -315,23 +315,7 @@ module SyntaxTree
|
|
315
315
|
|
316
316
|
# At the end, we're going to return whether or not this worker ever
|
317
317
|
# encountered an error.
|
318
|
-
|
319
|
-
with_workers(queue) do |item|
|
320
|
-
action.run(item)
|
321
|
-
false
|
322
|
-
rescue Parser::ParseError => error
|
323
|
-
warn("Error: #{error.message}")
|
324
|
-
highlight_error(error, item.source)
|
325
|
-
true
|
326
|
-
rescue Check::UnformattedError, Debug::NonIdempotentFormatError
|
327
|
-
true
|
328
|
-
rescue StandardError => error
|
329
|
-
warn(error.message)
|
330
|
-
warn(error.backtrace)
|
331
|
-
true
|
332
|
-
end
|
333
|
-
|
334
|
-
if errored
|
318
|
+
if process_queue(queue, action)
|
335
319
|
action.failure
|
336
320
|
1
|
337
321
|
else
|
@@ -342,13 +326,11 @@ module SyntaxTree
|
|
342
326
|
|
343
327
|
private
|
344
328
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
return yield queue.shift if queue.size == 1
|
349
|
-
|
329
|
+
# Processes each item in the queue with the given action. Returns whether
|
330
|
+
# or not any errors were encountered.
|
331
|
+
def process_queue(queue, action)
|
350
332
|
workers =
|
351
|
-
Etc.nprocessors.times.map do
|
333
|
+
[Etc.nprocessors, queue.size].min.times.map do
|
352
334
|
Thread.new do
|
353
335
|
# Propagate errors in the worker threads up to the parent thread.
|
354
336
|
Thread.current.abort_on_exception = true
|
@@ -360,7 +342,25 @@ module SyntaxTree
|
|
360
342
|
|
361
343
|
# While there is still work left to do, shift off the queue and
|
362
344
|
# process the item.
|
363
|
-
|
345
|
+
until queue.empty?
|
346
|
+
item = queue.shift
|
347
|
+
errored |=
|
348
|
+
begin
|
349
|
+
action.run(item)
|
350
|
+
false
|
351
|
+
rescue Parser::ParseError => error
|
352
|
+
warn("Error: #{error.message}")
|
353
|
+
highlight_error(error, item.source)
|
354
|
+
true
|
355
|
+
rescue Check::UnformattedError,
|
356
|
+
Debug::NonIdempotentFormatError
|
357
|
+
true
|
358
|
+
rescue StandardError => error
|
359
|
+
warn(error.message)
|
360
|
+
warn(error.backtrace)
|
361
|
+
true
|
362
|
+
end
|
363
|
+
end
|
364
364
|
|
365
365
|
# At the end, we're going to return whether or not this worker
|
366
366
|
# ever encountered an error.
|
@@ -368,7 +368,7 @@ module SyntaxTree
|
|
368
368
|
end
|
369
369
|
end
|
370
370
|
|
371
|
-
workers.inject(
|
371
|
+
workers.map(&:value).inject(:|)
|
372
372
|
end
|
373
373
|
|
374
374
|
# Highlights a snippet from a source and parse error.
|
@@ -68,7 +68,9 @@ module SyntaxTree
|
|
68
68
|
# going to just print out the node as it was seen in the source.
|
69
69
|
doc =
|
70
70
|
if leading.last&.ignore?
|
71
|
-
|
71
|
+
range = source[node.location.start_char...node.location.end_char]
|
72
|
+
separator = -> { breakable(indent: false, force: true) }
|
73
|
+
seplist(range.split(/\r?\n/, -1), separator) { |line| text(line) }
|
72
74
|
else
|
73
75
|
node.format(self)
|
74
76
|
end
|
@@ -2,20 +2,37 @@
|
|
2
2
|
|
3
3
|
module SyntaxTree
|
4
4
|
class LanguageServer
|
5
|
-
# This class provides inlay hints for the language server.
|
6
|
-
#
|
7
|
-
# is a little different for now.
|
8
|
-
#
|
9
|
-
# For more information, see the spec here:
|
5
|
+
# This class provides inlay hints for the language server. For more
|
6
|
+
# information, see the spec here:
|
10
7
|
# https://github.com/microsoft/language-server-protocol/issues/956.
|
11
|
-
#
|
12
8
|
class InlayHints < Visitor
|
13
|
-
|
9
|
+
# This represents a hint that is going to be displayed in the editor.
|
10
|
+
class Hint
|
11
|
+
attr_reader :line, :character, :label
|
12
|
+
|
13
|
+
def initialize(line:, character:, label:)
|
14
|
+
@line = line
|
15
|
+
@character = character
|
16
|
+
@label = label
|
17
|
+
end
|
18
|
+
|
19
|
+
# This is the shape that the LSP expects.
|
20
|
+
def to_json(*opts)
|
21
|
+
{
|
22
|
+
position: {
|
23
|
+
line: line,
|
24
|
+
character: character
|
25
|
+
},
|
26
|
+
label: label
|
27
|
+
}.to_json(*opts)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :stack, :hints
|
14
32
|
|
15
33
|
def initialize
|
16
34
|
@stack = []
|
17
|
-
@
|
18
|
-
@after = Hash.new { |hash, key| hash[key] = +"" }
|
35
|
+
@hints = []
|
19
36
|
end
|
20
37
|
|
21
38
|
def visit(node)
|
@@ -97,7 +114,11 @@ module SyntaxTree
|
|
97
114
|
#
|
98
115
|
def visit_rescue(node)
|
99
116
|
if node.exception.nil?
|
100
|
-
|
117
|
+
hints << Hint.new(
|
118
|
+
line: node.location.start_line - 1,
|
119
|
+
character: node.location.start_column + "rescue".length,
|
120
|
+
label: " StandardError"
|
121
|
+
)
|
101
122
|
end
|
102
123
|
|
103
124
|
super
|
@@ -120,17 +141,20 @@ module SyntaxTree
|
|
120
141
|
super
|
121
142
|
end
|
122
143
|
|
123
|
-
def self.find(program)
|
124
|
-
visitor = new
|
125
|
-
visitor.visit(program)
|
126
|
-
visitor
|
127
|
-
end
|
128
|
-
|
129
144
|
private
|
130
145
|
|
131
146
|
def parentheses(location)
|
132
|
-
|
133
|
-
|
147
|
+
hints << Hint.new(
|
148
|
+
line: location.start_line - 1,
|
149
|
+
character: location.start_column,
|
150
|
+
label: "₍"
|
151
|
+
)
|
152
|
+
|
153
|
+
hints << Hint.new(
|
154
|
+
line: location.end_line - 1,
|
155
|
+
character: location.end_column,
|
156
|
+
label: "₎"
|
157
|
+
)
|
134
158
|
end
|
135
159
|
end
|
136
160
|
end
|
@@ -20,71 +20,60 @@ module SyntaxTree
|
|
20
20
|
@output = output.binmode
|
21
21
|
end
|
22
22
|
|
23
|
+
# rubocop:disable Layout/LineLength
|
23
24
|
def run
|
24
25
|
store =
|
25
26
|
Hash.new do |hash, uri|
|
26
|
-
|
27
|
+
filepath = CGI.unescape(URI.parse(uri).path)
|
28
|
+
File.exist?(filepath) ? (hash[uri] = File.read(filepath)) : nil
|
27
29
|
end
|
28
30
|
|
29
31
|
while (headers = input.gets("\r\n\r\n"))
|
30
32
|
source = input.read(headers[/Content-Length: (\d+)/i, 1].to_i)
|
31
33
|
request = JSON.parse(source, symbolize_names: true)
|
32
34
|
|
35
|
+
# stree-ignore
|
33
36
|
case request
|
34
37
|
in { method: "initialize", id: }
|
35
38
|
store.clear
|
36
39
|
write(id: id, result: { capabilities: capabilities })
|
37
|
-
in method: "initialized"
|
40
|
+
in { method: "initialized" }
|
38
41
|
# ignored
|
39
|
-
in method: "shutdown" # tolerate missing ID to be a good citizen
|
42
|
+
in { method: "shutdown" } # tolerate missing ID to be a good citizen
|
40
43
|
store.clear
|
41
44
|
write(id: request[:id], result: {})
|
42
45
|
return
|
43
|
-
in {
|
44
|
-
method: "textDocument/didChange",
|
45
|
-
params: { textDocument: { uri: }, contentChanges: [{ text: }, *] }
|
46
|
-
}
|
46
|
+
in { method: "textDocument/didChange", params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } }
|
47
47
|
store[uri] = text
|
48
|
-
in {
|
49
|
-
method: "textDocument/didOpen",
|
50
|
-
params: { textDocument: { uri:, text: } }
|
51
|
-
}
|
48
|
+
in { method: "textDocument/didOpen", params: { textDocument: { uri:, text: } } }
|
52
49
|
store[uri] = text
|
53
|
-
in {
|
54
|
-
method: "textDocument/didClose", params: { textDocument: { uri: } }
|
55
|
-
}
|
50
|
+
in { method: "textDocument/didClose", params: { textDocument: { uri: } } }
|
56
51
|
store.delete(uri)
|
57
|
-
in {
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
write(id: id, result:
|
63
|
-
in {
|
64
|
-
method: "textDocument/inlayHints",
|
65
|
-
id:,
|
66
|
-
params: { textDocument: { uri: } }
|
67
|
-
}
|
68
|
-
write(id: id, result: inlay_hints(store[uri]))
|
69
|
-
in {
|
70
|
-
method: "syntaxTree/visualizing",
|
71
|
-
id:,
|
72
|
-
params: { textDocument: { uri: } }
|
73
|
-
}
|
52
|
+
in { method: "textDocument/formatting", id:, params: { textDocument: { uri: } } }
|
53
|
+
contents = store[uri]
|
54
|
+
write(id: id, result: contents ? [format(store[uri])] : nil)
|
55
|
+
in { method: "textDocument/inlayHint", id:, params: { textDocument: { uri: } } }
|
56
|
+
contents = store[uri]
|
57
|
+
write(id: id, result: contents ? inlay_hints(store[uri]) : nil)
|
58
|
+
in { method: "syntaxTree/visualizing", id:, params: { textDocument: { uri: } } }
|
74
59
|
write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +""))
|
75
|
-
in method: %r{\$/.+}
|
60
|
+
in { method: %r{\$/.+} }
|
76
61
|
# ignored
|
77
62
|
else
|
78
63
|
raise ArgumentError, "Unhandled: #{request}"
|
79
64
|
end
|
80
65
|
end
|
81
66
|
end
|
67
|
+
# rubocop:enable Layout/LineLength
|
82
68
|
|
83
69
|
private
|
84
70
|
|
85
71
|
def capabilities
|
86
72
|
{
|
87
73
|
documentFormattingProvider: true,
|
74
|
+
inlayHintProvider: {
|
75
|
+
resolveProvider: false
|
76
|
+
},
|
88
77
|
textDocumentSync: {
|
89
78
|
change: 1,
|
90
79
|
openClose: true
|
@@ -109,16 +98,13 @@ module SyntaxTree
|
|
109
98
|
end
|
110
99
|
|
111
100
|
def inlay_hints(source)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
{
|
116
|
-
before: inlay_hints.before.map(&serialize),
|
117
|
-
after: inlay_hints.after.map(&serialize)
|
118
|
-
}
|
101
|
+
visitor = InlayHints.new
|
102
|
+
SyntaxTree.parse(source).accept(visitor)
|
103
|
+
visitor.hints
|
119
104
|
rescue Parser::ParseError
|
120
105
|
# If there is a parse error, then we're not going to return any inlay
|
121
106
|
# hints for this source.
|
107
|
+
[]
|
122
108
|
end
|
123
109
|
|
124
110
|
def write(value)
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -1641,12 +1641,12 @@ module SyntaxTree
|
|
1641
1641
|
heredoc = @heredocs[-1]
|
1642
1642
|
|
1643
1643
|
location =
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1644
|
+
Location.token(
|
1645
|
+
line: lineno,
|
1646
|
+
char: char_pos,
|
1647
|
+
column: current_column,
|
1648
|
+
size: value.size + 1
|
1649
|
+
)
|
1650
1650
|
|
1651
1651
|
heredoc_end = HeredocEnd.new(value: value.chomp, location: location)
|
1652
1652
|
|
data/lib/syntax_tree/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syntax_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prettier_print
|