Skip to content

Commit

Permalink
Move codebase to typed strict (#127)
Browse files Browse the repository at this point in the history
* Move codebase to typed strict

* Add response generic to rubocop requests

* Add response generic to regular requests

* Improve block related types

* Require sorbet-runtime in check docs

* Fix rebase conflicts

* Remove class level run method

* Make CodeActions inherit from BaseRequest

* Use object as the return type of BaseRequest#run
  • Loading branch information
vinistock committed Jun 9, 2022
1 parent c594916 commit 2db6261
Show file tree
Hide file tree
Showing 34 changed files with 363 additions and 130 deletions.
11 changes: 10 additions & 1 deletion .rubocop.yml
Expand Up @@ -25,6 +25,15 @@ Sorbet/FalseSigil:
Sorbet/TrueSigil:
Enabled: true
Include:
- "**/*.rb"
- "test/**/*.rb"
Exclude:
- "**/*.rake"
- "lib/**/*.rb"

Sorbet/StrictSigil:
Enabled: true
Include:
- "lib/**/*.rb"
Exclude:
- "**/*.rake"
- "test/**/*.rb"
2 changes: 1 addition & 1 deletion lib/internal.rb
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

require "sorbet-runtime"
Expand Down
4 changes: 2 additions & 2 deletions lib/ruby-lsp.rb
@@ -1,6 +1,6 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module RubyLsp
VERSION = File.read(File.expand_path("../VERSION", __dir__)).strip
VERSION = T.let(File.read(File.expand_path("../VERSION", __dir__)).strip, String)
end
8 changes: 7 additions & 1 deletion lib/ruby_lsp/document.rb
Expand Up @@ -32,7 +32,13 @@ def ==(other)
@source == other.source
end

sig { params(request_name: Symbol, block: T.proc.params(document: Document).returns(T.untyped)).returns(T.untyped) }
sig do
type_parameters(:T)
.params(
request_name: Symbol,
block: T.proc.params(document: Document).returns(T.type_parameter(:T))
).returns(T.type_parameter(:T))
end
def cache_fetch(request_name, &block)
cached = @cache[request_name]
return cached if cached
Expand Down
33 changes: 21 additions & 12 deletions lib/ruby_lsp/handler.rb
Expand Up @@ -114,26 +114,26 @@ def respond_with_capabilities(enabled_features)
sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
def respond_with_document_symbol(uri)
store.cache_fetch(uri, :document_symbol) do |document|
RubyLsp::Requests::DocumentSymbol.run(document)
RubyLsp::Requests::DocumentSymbol.new(document).run
end
end

sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::FoldingRange]) }
def respond_with_folding_ranges(uri)
store.cache_fetch(uri, :folding_ranges) do |document|
Requests::FoldingRanges.run(document)
Requests::FoldingRanges.new(document).run
end
end

sig do
params(
uri: String,
positions: T::Array[Document::PositionShape]
).returns(T::Array[RubyLsp::Requests::Support::SelectionRange])
).returns(T::Array[T.nilable(RubyLsp::Requests::Support::SelectionRange)])
end
def respond_with_selection_ranges(uri, positions)
ranges = store.cache_fetch(uri, :selection_ranges) do |document|
Requests::SelectionRanges.run(document)
Requests::SelectionRanges.new(document).run
end

# Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
Expand All @@ -150,19 +150,22 @@ def respond_with_selection_ranges(uri, positions)
sig { params(uri: String).returns(LanguageServer::Protocol::Interface::SemanticTokens) }
def respond_with_semantic_highlighting(uri)
store.cache_fetch(uri, :semantic_highlighting) do |document|
Requests::SemanticHighlighting.new(document, encoder: Requests::Support::SemanticTokenEncoder.new).run
T.cast(
Requests::SemanticHighlighting.new(document, encoder: Requests::Support::SemanticTokenEncoder.new).run,
LanguageServer::Protocol::Interface::SemanticTokens
)
end
end

sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
sig { params(uri: String).returns(T.nilable(T::Array[LanguageServer::Protocol::Interface::TextEdit])) }
def respond_with_formatting(uri)
Requests::Formatting.run(uri, store.get(uri))
Requests::Formatting.new(uri, store.get(uri)).run
end

sig { params(uri: String).void }
def send_diagnostics(uri)
response = store.cache_fetch(uri, :diagnostics) do |document|
Requests::Diagnostics.run(uri, document)
Requests::Diagnostics.new(uri, document).run
end

@writer.write(
Expand All @@ -175,11 +178,11 @@ def send_diagnostics(uri)
end

sig do
params(uri: String, range: T::Range[Integer]).returns(T::Array[LanguageServer::Protocol::Interface::Diagnostic])
params(uri: String, range: T::Range[Integer]).returns(T::Array[LanguageServer::Protocol::Interface::CodeAction])
end
def respond_with_code_actions(uri, range)
store.cache_fetch(uri, :code_actions) do |document|
Requests::CodeActions.run(uri, document, range)
Requests::CodeActions.new(uri, document, range).run
end
end

Expand All @@ -190,10 +193,16 @@ def respond_with_code_actions(uri, range)
).returns(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
end
def respond_with_document_highlight(uri, position)
Requests::DocumentHighlight.run(store.get(uri), position)
Requests::DocumentHighlight.new(store.get(uri), position).run
end

sig { params(request: T::Hash[Symbol, T.untyped], block: T.proc.void).returns(T.untyped) }
sig do
type_parameters(:T)
.params(
request: T::Hash[Symbol, T.untyped],
block: T.proc.returns(T.type_parameter(:T))
).returns(T.type_parameter(:T))
end
def with_telemetry(request, &block)
result = T.let(nil, T.untyped)
error = T.let(nil, T.nilable(StandardError))
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_lsp/requests.rb
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module RubyLsp
Expand Down
16 changes: 9 additions & 7 deletions lib/ruby_lsp/requests/base_request.rb
@@ -1,24 +1,26 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module RubyLsp
module Requests
# :nodoc:
class BaseRequest < SyntaxTree::Visitor
def self.run(document)
new(document).run
end
extend T::Sig
extend T::Helpers

abstract!

sig { params(document: Document).void }
def initialize(document)
@document = document

super()
end

def run
raise NotImplementedError, "#{self.class}#run must be implemented"
end
sig { abstract.returns(Object) }
def run; end

sig { params(node: SyntaxTree::Node).returns(LanguageServer::Protocol::Interface::Range) }
def range_from_syntax_tree_node(node)
loc = node.location

Expand Down
27 changes: 18 additions & 9 deletions lib/ruby_lsp/requests/code_actions.rb
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module RubyLsp
Expand All @@ -14,23 +14,32 @@ module Requests
# puts "Hello" # --> code action: quick fix indentation
# end
# ```
class CodeActions
def self.run(uri, document, range)
new(uri, document, range).run
end
class CodeActions < BaseRequest
extend T::Sig

sig do
params(
uri: String,
document: Document,
range: T::Range[Integer]
).void
end
def initialize(uri, document, range)
@document = document
super(document)

@uri = uri
@range = range
end

sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::CodeAction], Object)) }
def run
diagnostics = Diagnostics.run(@uri, @document)
corrections = diagnostics.select { |diagnostic| diagnostic.correctable? && diagnostic.in_range?(@range) }
diagnostics = Diagnostics.new(@uri, @document).run
corrections = diagnostics.select do |diagnostic|
diagnostic.correctable? && T.cast(diagnostic, Support::RuboCopDiagnostic).in_range?(@range)
end
return [] if corrections.empty?

corrections.map!(&:to_lsp_code_action)
T.cast(corrections, T::Array[Support::RuboCopDiagnostic]).map!(&:to_lsp_code_action)
end
end
end
Expand Down
14 changes: 13 additions & 1 deletion lib/ruby_lsp/requests/diagnostics.rb
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module RubyLsp
Expand All @@ -15,6 +15,16 @@ module Requests
# end
# ```
class Diagnostics < RuboCopRequest
extend T::Sig

sig do
override.returns(
T.any(
T.all(T::Array[Support::RuboCopDiagnostic], Object),
T.all(T::Array[Support::SyntaxErrorDiagnostic], Object),
)
)
end
def run
return syntax_error_diagnostics if @document.syntax_errors?

Expand All @@ -23,12 +33,14 @@ def run
@diagnostics
end

sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
def file_finished(_file, offenses)
@diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, @uri) }
end

private

sig { returns(T::Array[Support::SyntaxErrorDiagnostic]) }
def syntax_error_diagnostics
@document.syntax_error_edits.map { |e| Support::SyntaxErrorDiagnostic.new(e) }
end
Expand Down
27 changes: 21 additions & 6 deletions lib/ruby_lsp/requests/document_highlight.rb
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module RubyLsp
Expand All @@ -21,18 +21,28 @@ module Requests
# end
# ```
class DocumentHighlight < BaseRequest
def self.run(document, position)
new(document, position).run
extend T::Sig

VarNodes = T.type_alias do
T.any(
SyntaxTree::GVar,
SyntaxTree::Ident,
SyntaxTree::IVar,
SyntaxTree::Const,
SyntaxTree::CVar
)
end

sig { params(document: Document, position: Document::PositionShape).void }
def initialize(document, position)
@highlights = []
@highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
position = Document::Scanner.new(document.source).find_position(position)
@target = find(document.tree, position)
@target = T.let(find(document.tree, position), T.nilable(VarNodes))

super(document)
end

sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight], Object)) }
def run
# no @target means the target is not highlightable
return [] unless @target
Expand All @@ -41,6 +51,7 @@ def run
@highlights
end

sig { params(node: SyntaxTree::VarField).void }
def visit_var_field(node)
if matches_target?(node.value)
add_highlight(
Expand All @@ -52,6 +63,7 @@ def visit_var_field(node)
super
end

sig { params(node: SyntaxTree::VarRef).void }
def visit_var_ref(node)
if matches_target?(node.value)
add_highlight(
Expand All @@ -65,6 +77,7 @@ def visit_var_ref(node)

private

sig { params(node: SyntaxTree::Node, position: Integer).returns(T.nilable(VarNodes)) }
def find(node, position)
matched =
node.child_nodes.compact.bsearch do |child|
Expand All @@ -83,10 +96,12 @@ def find(node, position)
end
end

sig { params(node: SyntaxTree::Node).returns(T::Boolean) }
def matches_target?(node)
node.is_a?(@target.class) && node.value == @target.value
node.is_a?(@target.class) && T.cast(node, VarNodes).value == T.must(@target).value
end

sig { params(node: SyntaxTree::Node, kind: Integer).void }
def add_highlight(node, kind)
range = range_from_syntax_tree_node(node)
@highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: kind)
Expand Down

0 comments on commit 2db6261

Please sign in to comment.