Skip to content

Commit ba2a6e6

Browse files
committed
Add inlay hint support per LSP specification. Closes #103.
1 parent cc38cf4 commit ba2a6e6

File tree

3 files changed

+53
-10
lines changed

3 files changed

+53
-10
lines changed

lib/syntax_tree/language_server.rb

+21-6
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,19 @@ def run
6161
}
6262
write(id: id, result: [format(store[uri])])
6363
in {
64+
# official RPC in LSP spec 3.17
65+
method: "textDocument/inlayHint",
66+
id:,
67+
params: { textDocument: { uri: } }
68+
}
69+
write(id: id, result: inlay_hints(store[uri], false))
70+
in {
71+
# proprietary RPC (deprecated) between this gem and vscode-syntax-tree
6472
method: "textDocument/inlayHints",
6573
id:,
6674
params: { textDocument: { uri: } }
6775
}
68-
write(id: id, result: inlay_hints(store[uri]))
76+
write(id: id, result: inlay_hints(store[uri], true))
6977
in {
7078
method: "syntaxTree/visualizing",
7179
id:,
@@ -85,6 +93,9 @@ def run
8593
def capabilities
8694
{
8795
documentFormattingProvider: true,
96+
inlayHintProvider: {
97+
resolveProvider: false
98+
},
8899
textDocumentSync: {
89100
change: 1,
90101
openClose: true
@@ -108,14 +119,18 @@ def format(source)
108119
}
109120
end
110121

111-
def inlay_hints(source)
122+
def inlay_hints(source, proprietary)
112123
inlay_hints = InlayHints.find(SyntaxTree.parse(source))
113124
serialize = ->(position, text) { { position: position, text: text } }
114125

115-
{
116-
before: inlay_hints.before.map(&serialize),
117-
after: inlay_hints.after.map(&serialize)
118-
}
126+
if proprietary
127+
{
128+
before: inlay_hints.before.map(&serialize),
129+
after: inlay_hints.after.map(&serialize)
130+
}
131+
else
132+
inlay_hints.all
133+
end
119134
rescue Parser::ParseError
120135
# If there is a parse error, then we're not going to return any inlay
121136
# hints for this source.

lib/syntax_tree/language_server/inlay_hints.rb

+26-4
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22

33
module SyntaxTree
44
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.
5+
# This class provides inlay hints for the language server. It existed
6+
# before the spec was finalized so, so it provides two result formats:
7+
# aligned with the spec (`#all`) and proprietary (`#before` and `#after`).
88
#
99
# For more information, see the spec here:
1010
# https://github.com/microsoft/language-server-protocol/issues/956.
1111
#
1212
class InlayHints < Visitor
13-
attr_reader :stack, :before, :after
13+
attr_reader :stack, :all, :before, :after
1414

1515
def initialize
1616
@stack = []
17+
@all = []
1718
@before = Hash.new { |hash, key| hash[key] = +"" }
1819
@after = Hash.new { |hash, key| hash[key] = +"" }
1920
end
@@ -98,6 +99,13 @@ def visit_if_op(node)
9899
def visit_rescue(node)
99100
if node.exception.nil?
100101
after[node.location.start_char + "rescue".length] << " StandardError"
102+
all << {
103+
position: {
104+
line: node.location.start_line - 1,
105+
character: node.location.start_column + "rescue".length
106+
},
107+
label: " StandardError"
108+
}
101109
end
102110

103111
super
@@ -129,6 +137,20 @@ def self.find(program)
129137
private
130138

131139
def parentheses(location)
140+
all << {
141+
position: {
142+
line: location.start_line - 1,
143+
character: location.start_column
144+
},
145+
label: "₍"
146+
}
147+
all << {
148+
position: {
149+
line: location.end_line - 1,
150+
character: location.end_column
151+
},
152+
label: "₎"
153+
}
132154
before[location.start_char] << "₍"
133155
after[location.end_char] << "₎"
134156
end

test/language_server/inlay_hints_test.rb

+6
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,46 @@ def test_assignments_in_parameters
1111

1212
assert_equal(1, hints.before.length)
1313
assert_equal(1, hints.after.length)
14+
assert_equal(2, hints.all.length)
1415
end
1516

1617
def test_operators_in_binaries
1718
hints = find("1 + 2 * 3")
1819

1920
assert_equal(1, hints.before.length)
2021
assert_equal(1, hints.after.length)
22+
assert_equal(2, hints.all.length)
2123
end
2224

2325
def test_binaries_in_assignments
2426
hints = find("a = 1 + 2")
2527

2628
assert_equal(1, hints.before.length)
2729
assert_equal(1, hints.after.length)
30+
assert_equal(2, hints.all.length)
2831
end
2932

3033
def test_nested_ternaries
3134
hints = find("a ? b : c ? d : e")
3235

3336
assert_equal(1, hints.before.length)
3437
assert_equal(1, hints.after.length)
38+
assert_equal(2, hints.all.length)
3539
end
3640

3741
def test_bare_rescue
3842
hints = find("begin; rescue; end")
3943

4044
assert_equal(1, hints.after.length)
45+
assert_equal(1, hints.all.length)
4146
end
4247

4348
def test_unary_in_binary
4449
hints = find("-a + b")
4550

4651
assert_equal(1, hints.before.length)
4752
assert_equal(1, hints.after.length)
53+
assert_equal(2, hints.all.length)
4854
end
4955

5056
private

0 commit comments

Comments
 (0)