Skip to content

Commit

Permalink
Migrate to YARP (#1025)
Browse files Browse the repository at this point in the history
* Temporarily disable .github/workflows/lsp_check.yml

* Temporarily disable some tests

* Preparation for YARP migration

* Migrate DocumentSymbol request to YARP

* Migrate Hover request to YARP

* Migrate PathCompletion request to YARP

* Migrate Sorbet request to YARP

* Migrate InlayHints request to YARP

* Migrate ShowSyntaxTree request to YARP

* Migrate SelectionRange request to YARP

* Migrate Definition request to YARP

* Migrate Diagnostics request to YARP

* Migrate CodeActionResolve request to YARP

* Update selection ranges expectations

* Fix rebase discrepancies

* Add YARP RBI annotations (#978)

* Migrate code lens to yarp (#979)

Migrate CodeLens to YARP

* Update `SERVER_EXTENSIONS.md` for YARP (#984)

Co-authored-by: Andy Waite <andyw8@users.noreply.github.com>

* Update text references from Syntax Tree to YARP (#986)

Co-authored-by: Andy Waite <andyw8@users.noreply.github.com>

* Fix rebase discrepancies

* Migrate folding range to YARP (#980)

* Fix rebase discrepancies 3

* Migrate semantic highlighting to YARP (#1000)

Co-authored-by: Alexandre Terrasa <Morriar@users.noreply.github.com>

* Migrate document highlight to YARP (#1005)

* Simplify block locals handling in semantic highlighting (#1011)

* Fix latest breaking changes

* Make folding range a listener (#1013)

* Migrate DocumentLink request to YARP (#982)

* Migrate DocumentLink to YARP

* Only parse comments where needed

* Fix type

* PR feedback

* Fix argument order

* Re-enable DocumentLink integration test

---------

Co-authored-by: Andy Waite <andyw8@users.noreply.github.com>

* Re-enable CI (#1022)

* Re-generate YARP RBIs

* Re-enable tests

* Fix typechecking errors

* Use locations for creating document symbol entries

---------

Co-authored-by: Andy Waite <andyw8@users.noreply.github.com>
Co-authored-by: Andy Waite <13400+andyw8@users.noreply.github.com>
Co-authored-by: Alexandre Terrasa <Morriar@users.noreply.github.com>
  • Loading branch information
4 people committed Sep 19, 2023
1 parent 19f9a97 commit 78a1876
Show file tree
Hide file tree
Showing 96 changed files with 6,623 additions and 16,904 deletions.
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

0 comments on commit 78a1876

Please sign in to comment.