Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit fc93026

Browse files
committed
Rename implicits to inlay hints
1 parent 8e73f50 commit fc93026

File tree

5 files changed

+106
-225
lines changed

5 files changed

+106
-225
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1212

1313
- Changed `SyntaxTree` from being a class to being a module. The parser functionality is moved into `SyntaxTree::Parser`.
1414
- There is now a parent class for all of the nodes named `SyntaxTree::Node`.
15+
- The `Implicits` class has been renamed to `InlayHints` to match the new LSP spec.
16+
17+
### Removed
18+
19+
- The disassembly code action has been removed to limit the scope of this project overall.
1520

1621
## [1.2.0] - 2022-01-09
1722

lib/syntax_tree/code_actions.rb

Lines changed: 0 additions & 89 deletions
This file was deleted.

lib/syntax_tree/implicits.rb

Lines changed: 0 additions & 91 deletions
This file was deleted.

lib/syntax_tree/language_server.rb

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
require "json"
55
require "uri"
66

7-
require_relative "code_actions"
8-
require_relative "implicits"
7+
require_relative "language_server/inlay_hints"
98

109
module SyntaxTree
1110
class LanguageServer
@@ -35,8 +34,6 @@ def run
3534
in { method: "shutdown" }
3635
store.clear
3736
return
38-
in { method: "textDocument/codeAction", id:, params: { textDocument: { uri: }, range: { start: { line: } } } }
39-
write(id: id, result: code_actions(store[uri], line + 1))
4037
in { method: "textDocument/didChange", params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } }
4138
store[uri] = text
4239
in { method: "textDocument/didOpen", params: { textDocument: { uri:, text: } } }
@@ -45,10 +42,8 @@ def run
4542
store.delete(uri)
4643
in { method: "textDocument/formatting", id:, params: { textDocument: { uri: } } }
4744
write(id: id, result: [format(store[uri])])
48-
in { method: "syntaxTree/disasm", id:, params: { textDocument: { uri:, query: { line:, name: } } } }
49-
write(id: id, result: disasm(store[uri], line.to_i, name))
50-
in { method: "syntaxTree/implicits", id:, params: { textDocument: { uri: } } }
51-
write(id: id, result: implicits(store[uri]))
45+
in { method: "textDocument/inlayHints", id:, params: { textDocument: { uri: } } }
46+
write(id: id, result: inlay_hints(store[uri]))
5247
in { method: "syntaxTree/visualizing", id:, params: { textDocument: { uri: } } }
5348
output = []
5449
PP.pp(SyntaxTree.parse(store[uri]), output)
@@ -65,43 +60,11 @@ def run
6560

6661
def capabilities
6762
{
68-
codeActionProvider: { codeActionsKinds: ["disasm"] },
6963
documentFormattingProvider: true,
7064
textDocumentSync: { change: 1, openClose: true }
7165
}
7266
end
7367

74-
def code_actions(source, line)
75-
actions = CodeActions.find(SyntaxTree.parse(source), line).actions
76-
log("Found #{actions.length} actions on line #{line}")
77-
78-
actions.map(&:as_json)
79-
end
80-
81-
def disasm(source, line, name)
82-
actions = CodeActions.find(SyntaxTree.parse(source), line).actions
83-
log("Disassembling #{name.inspect} on line #{line.inspect}")
84-
85-
matched = actions.detect { |action| action.is_a?(CodeActions::DisasmAction) && action.node.name.value == name }
86-
return "Unable to find method: #{name}" unless matched
87-
88-
# First, get an instruction sequence that encompasses the method that
89-
# we're going to disassemble. It will include the method declaration,
90-
# which will be the top instruction sequence.
91-
location = matched.node.location
92-
iseq = RubyVM::InstructionSequence.new(source[location.start_char...location.end_char])
93-
94-
# Next, get the first child. We do this because the parent instruction
95-
# sequence is the method declaration, whereas the first child is the body
96-
# of the method, which is what we're interested in.
97-
method = nil
98-
iseq.each_child { |child| method = child }
99-
100-
# Finally, return the disassembly as a string to the server, which will
101-
# serialize it to JSON and return it back to the client.
102-
method.disasm
103-
end
104-
10568
def format(source)
10669
{
10770
range: {
@@ -116,15 +79,15 @@ def log(message)
11679
write(method: "window/logMessage", params: { type: 4, message: message })
11780
end
11881

119-
def implicits(source)
120-
implicits = Implicits.find(SyntaxTree.parse(source))
82+
def inlay_hints(source)
83+
inlay_hints = InlayHints.find(SyntaxTree.parse(source))
12184
serialize = ->(position, text) { { position: position, text: text } }
12285

12386
{
124-
before: implicits.before.map(&serialize),
125-
after: implicits.after.map(&serialize)
87+
before: inlay_hints.before.map(&serialize),
88+
after: inlay_hints.after.map(&serialize)
12689
}
127-
rescue ParseError
90+
rescue Parser::ParseError
12891
end
12992

13093
def write(value)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
module SyntaxTree
4+
class LanguageServer
5+
class InlayHints
6+
attr_reader :before, :after
7+
8+
def initialize
9+
@before = Hash.new { |hash, key| hash[key] = +"" }
10+
@after = Hash.new { |hash, key| hash[key] = +"" }
11+
end
12+
13+
# Adds the implicitly rescued StandardError into a bare rescue clause. For
14+
# example,
15+
#
16+
# begin
17+
# rescue
18+
# end
19+
#
20+
# becomes
21+
#
22+
# begin
23+
# rescue StandardError
24+
# end
25+
#
26+
def bare_rescue(location)
27+
after[location.start_char + "rescue".length] << " StandardError"
28+
end
29+
30+
# Adds the implicitly referenced value (local variable or method call)
31+
# that is added into a hash when the value of a key-value pair is omitted.
32+
# For example,
33+
#
34+
# { value: }
35+
#
36+
# becomes
37+
#
38+
# { value: value }
39+
#
40+
def missing_hash_value(key, location)
41+
after[location.end_char] << " #{key}"
42+
end
43+
44+
# Adds implicit parentheses around certain expressions to make it clear
45+
# which subexpression will be evaluated first. For example,
46+
#
47+
# a + b * c
48+
#
49+
# becomes
50+
#
51+
# a + ₍b * c₎
52+
#
53+
def precedence_parentheses(location)
54+
before[location.start_char] << "₍"
55+
after[location.end_char] << "₎"
56+
end
57+
58+
def self.find(program)
59+
inlay_hints = new
60+
queue = [[nil, program]]
61+
62+
until queue.empty?
63+
parent_node, child_node = queue.shift
64+
65+
child_node.child_nodes.each do |grand_child_node|
66+
queue << [child_node, grand_child_node] if grand_child_node
67+
end
68+
69+
case [parent_node, child_node]
70+
in _, Rescue[exception: nil, location:]
71+
inlay_hints.bare_rescue(location)
72+
in _, Assoc[key: Label[value: key], value: nil, location:]
73+
inlay_hints.missing_hash_value(key[0...-1], location)
74+
in Assign | Binary | IfOp | OpAssign, IfOp[location:]
75+
inlay_hints.precedence_parentheses(location)
76+
in Assign | OpAssign, Binary[location:]
77+
inlay_hints.precedence_parentheses(location)
78+
in Binary[operator: parent_oper], Binary[operator: child_oper, location:] if parent_oper != child_oper
79+
inlay_hints.precedence_parentheses(location)
80+
in Binary, Unary[operator: "-", location:]
81+
inlay_hints.precedence_parentheses(location)
82+
in Params, Assign[location:]
83+
inlay_hints.precedence_parentheses(location)
84+
else
85+
# do nothing
86+
end
87+
end
88+
89+
inlay_hints
90+
end
91+
end
92+
end
93+
end

0 commit comments

Comments
 (0)