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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad3c35843b6e3148499ac001d05a9c5d613d69debff5fdc411dff89a9adc5ca4
4
- data.tar.gz: 570ae99edf8b17b5d205142872621b0551632d29ac4671ee14fed8415cf9cfe4
3
+ metadata.gz: 499aba04c732c3101f3e3ebb7d487a19842781a0a97b67fad047c2cf9bbe2bcb
4
+ data.tar.gz: dc9e28898951ae74f3a053b261b8d1e752567917bbb8eb34a4aa22af6b49b716
5
5
  SHA512:
6
- metadata.gz: 7ec0e9d8f5f4f828ba0d451d49bd7fa9f5576570f7427c91973503e795fe068e21a61dac4f1880be12dbd551855eb0b8d27f359a9a682b964f3183aaf687e1da
7
- data.tar.gz: b8ae5c0132ebc445d060b1d5cef477253c724ea2c7a22a536d4ea78b3fb2d905c560e1f044f8843b92f79e0dbc1a434cda2a38e76b2732b426719150785a59af
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/v2.8.0...HEAD
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 (2.8.0)
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
- minitest (5.16.0)
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.30.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.1.0)
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/inlayHints](#textdocumentinlayhints)
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/inlayHints
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. However, to ease mental overhead, our language server includes small parentheses to make this explicit, as in:
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₎
@@ -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
- errored =
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
- def with_workers(queue)
346
- # If the queue is just 1 item, then we're not going to bother going
347
- # through the whole ceremony of parallelizing the work.
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
- (errored ||= yield queue.shift) until queue.empty?
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(false) { |accum, thread| accum || thread.value }
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
- text(source[node.location.start_char...node.location.end_char])
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. It is loosely
6
- # designed around the LSP spec, but existed before the spec was finalized so
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
- attr_reader :stack, :before, :after
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
- @before = Hash.new { |hash, key| hash[key] = +"" }
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
- after[node.location.start_char + "rescue".length] << " StandardError"
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
- before[location.start_char] << "₍"
133
- after[location.end_char] << "₎"
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
- hash[uri] = File.binread(CGI.unescape(URI.parse(uri).path))
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
- method: "textDocument/formatting",
59
- id:,
60
- params: { textDocument: { uri: } }
61
- }
62
- write(id: id, result: [format(store[uri])])
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
- inlay_hints = InlayHints.find(SyntaxTree.parse(source))
113
- serialize = ->(position, text) { { position: position, text: text } }
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)
@@ -1641,12 +1641,12 @@ module SyntaxTree
1641
1641
  heredoc = @heredocs[-1]
1642
1642
 
1643
1643
  location =
1644
- Location.token(
1645
- line: lineno,
1646
- char: char_pos,
1647
- column: current_column,
1648
- size: value.size + 1
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "2.8.0"
4
+ VERSION = "3.0.1"
5
5
  end
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: 2.8.0
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-06-21 00:00:00.000000000 Z
11
+ date: 2022-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prettier_print