Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to YARP #1025

Merged
merged 30 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ef14826
Temporarily disable .github/workflows/lsp_check.yml
andyw8 Sep 6, 2023
f2165b4
Temporarily disable some tests
andyw8 Sep 6, 2023
e7a0b0f
Preparation for YARP migration
andyw8 Sep 6, 2023
7b75f5b
Migrate DocumentSymbol request to YARP
andyw8 Sep 6, 2023
60b3f96
Migrate Hover request to YARP
andyw8 Sep 6, 2023
e1633d4
Migrate PathCompletion request to YARP
andyw8 Sep 6, 2023
eb422eb
Migrate Sorbet request to YARP
andyw8 Sep 6, 2023
f2e5da9
Migrate InlayHints request to YARP
andyw8 Sep 6, 2023
346b73a
Migrate ShowSyntaxTree request to YARP
andyw8 Sep 6, 2023
c996e22
Migrate SelectionRange request to YARP
andyw8 Sep 6, 2023
367ee27
Migrate Definition request to YARP
andyw8 Sep 6, 2023
114ec04
Migrate Diagnostics request to YARP
andyw8 Sep 6, 2023
d334d85
Migrate CodeActionResolve request to YARP
andyw8 Sep 6, 2023
a1ef49a
Update selection ranges expectations
andyw8 Sep 6, 2023
e5c3a7e
Fix rebase discrepancies
vinistock Sep 6, 2023
b0fed96
Add YARP RBI annotations (#978)
vinistock Sep 6, 2023
4358edf
Migrate code lens to yarp (#979)
vinistock Sep 7, 2023
b65f5e8
Update `SERVER_EXTENSIONS.md` for YARP (#984)
andyw8 Sep 7, 2023
991af84
Update text references from Syntax Tree to YARP (#986)
andyw8 Sep 8, 2023
6632698
Fix rebase discrepancies
vinistock Sep 8, 2023
25b3f7d
Migrate folding range to YARP (#980)
vinistock Sep 11, 2023
934f872
Fix rebase discrepancies 3
vinistock Sep 13, 2023
bf5a566
Migrate semantic highlighting to YARP (#1000)
vinistock Sep 15, 2023
76bd90f
Migrate document highlight to YARP (#1005)
vinistock Sep 15, 2023
a0246cd
Simplify block locals handling in semantic highlighting (#1011)
vinistock Sep 15, 2023
e4912b0
Fix latest breaking changes
vinistock Sep 18, 2023
ced0976
Make folding range a listener (#1013)
vinistock Sep 18, 2023
fb20c0b
Migrate DocumentLink request to YARP (#982)
andyw8 Sep 18, 2023
ba026c5
Re-enable CI (#1022)
vinistock Sep 18, 2023
6198553
Use locations for creating document symbol entries
vinistock Sep 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ PATH
language_server-protocol (~> 3.17.0)
sorbet-runtime
syntax_tree (>= 6.1.1, < 7)
yarp (>= 0.11, < 0.13)
yarp (>= 0.12, < 0.13)

GEM
remote: https://rubygems.org/
Expand Down
10 changes: 5 additions & 5 deletions SERVER_EXTENSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,18 @@ module RubyLsp
@_response = T.let(nil, ResponseType)
@config = config

# Register that this listener will handle `on_const` events (i.e.: whenever a constant is found in the code)
emitter.register(self, :on_const)
# Register that this listener will handle `on_constant_read` events (i.e.: whenever a constant read is found in the code)
emitter.register(self, :on_constant_read)
end

# Listeners must define methods for each event they registered with the emitter. In this case, we have to define
# `on_const` to specify what this listener should do every time we find a constant
sig { params(node: SyntaxTree::Const).void }
def on_const(node)
sig { params(node: YARP::ConstantReadNode).void }
def on_constant_read(node)
# Certain helpers are made available to listeners to build LSP responses. The classes under `RubyLsp::Interface`
# are generally used to build responses and they match exactly what the specification requests.
contents = RubyLsp::Interface::MarkupContent.new(kind: "markdown", value: "Hello!")
@_response = RubyLsp::Interface::Hover.new(range: range_from_syntax_tree_node(node), contents: contents)
@_response = RubyLsp::Interface::Hover.new(range: range_from_node(node), contents: contents)
end
end
end
Expand Down
65 changes: 31 additions & 34 deletions lib/ruby_lsp/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class Document
RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
EditShape = T.type_alias { { range: RangeShape, text: String } }

sig { returns(T.nilable(SyntaxTree::Node)) }
attr_reader :tree
sig { returns(YARP::ParseResult) }
attr_reader :parse_result

sig { returns(String) }
attr_reader :source
Expand All @@ -29,10 +29,17 @@ def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind
@version = T.let(version, Integer)
@uri = T.let(uri, URI::Generic)
@unparsed_edits = T.let([], T::Array[EditShape])
@syntax_error = T.let(false, T::Boolean)
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
rescue SyntaxTree::Parser::ParseError
@syntax_error = true
@parse_result = T.let(YARP.parse(@source), YARP::ParseResult)
end

sig { returns(YARP::ProgramNode) }
def tree
@parse_result.value
end

sig { returns(T::Array[YARP::Comment]) }
def comments
@parse_result.comments
end

sig { params(other: Document).returns(T::Boolean) }
Expand Down Expand Up @@ -89,20 +96,12 @@ def parse
return if @unparsed_edits.empty?

@unparsed_edits.clear
@tree = SyntaxTree.parse(@source)
@syntax_error = false
rescue SyntaxTree::Parser::ParseError
@syntax_error = true
@parse_result = YARP.parse(@source)
end

sig { returns(T::Boolean) }
def syntax_error?
@syntax_error
end

sig { returns(T::Boolean) }
def parsed?
!@tree.nil?
@parse_result.failure?
end

sig { returns(Scanner) }
Expand All @@ -113,27 +112,25 @@ def create_scanner
sig do
params(
position: PositionShape,
node_types: T::Array[T.class_of(SyntaxTree::Node)],
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
node_types: T::Array[T.class_of(YARP::Node)],
).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
end
def locate_node(position, node_types: [])
return [nil, nil, []] unless parsed?

locate(T.must(@tree), create_scanner.find_char_position(position), node_types: node_types)
locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
end

sig do
params(
node: SyntaxTree::Node,
node: YARP::Node,
char_position: Integer,
node_types: T::Array[T.class_of(SyntaxTree::Node)],
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
node_types: T::Array[T.class_of(YARP::Node)],
).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
end
def locate(node, char_position, node_types: [])
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(YARP::Node)])
closest = node
parent = T.let(nil, T.nilable(SyntaxTree::Node))
nesting = T.let([], T::Array[T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)])
parent = T.let(nil, T.nilable(YARP::Node))
nesting = T.let([], T::Array[T.any(YARP::ClassNode, YARP::ModuleNode)])

until queue.empty?
candidate = queue.shift
Expand All @@ -144,24 +141,24 @@ def locate(node, char_position, node_types: [])
# Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
# same order as the visiting mechanism, which means searching the child nodes before moving on to the next
# sibling
queue.unshift(*candidate.child_nodes)
T.unsafe(queue).unshift(*candidate.child_nodes)

# Skip if the current node doesn't cover the desired position
loc = candidate.location
next unless (loc.start_char...loc.end_char).cover?(char_position)
next unless (loc.start_offset...loc.end_offset).cover?(char_position)

# If the node's start character is already past the position, then we should've found the closest node
# already
break if char_position < loc.start_char
break if char_position < loc.start_offset

# If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
# need to pop the stack
previous_level = nesting.last
nesting.pop if previous_level && candidate.start_char > previous_level.end_char
nesting.pop if previous_level && loc.start_offset > previous_level.location.end_offset

# Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
# target when it is a constant
if candidate.is_a?(SyntaxTree::ClassDeclaration) || candidate.is_a?(SyntaxTree::ModuleDeclaration)
if candidate.is_a?(YARP::ClassNode) || candidate.is_a?(YARP::ModuleNode)
nesting << candidate
end

Expand All @@ -170,13 +167,13 @@ def locate(node, char_position, node_types: [])

# If the current node is narrower than or equal to the previous closest node, then it is more precise
closest_loc = closest.location
if loc.end_char - loc.start_char <= closest_loc.end_char - closest_loc.start_char
if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
parent = closest
closest = candidate
end
end

[closest, parent, nesting.map { |n| n.constant.constant.value }]
[closest, parent, nesting.map { |n| n.constant_path.location.slice }]
end

class Scanner
Expand Down