ruby-lsp 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f81baa8f09011e5130c955c4c5d2a33583379b0b073caee556719b874efc320e
4
- data.tar.gz: 55a664f923d93bda9ddb06624303f889123496c672c46b31ea71c629cfa96a81
3
+ metadata.gz: d067270389ee6b02bb03fb6363e272b6808e7c3d27d77354442801d09a617149
4
+ data.tar.gz: 74e7cd206a790a8c139969f5167ab3d38686f78646c619488153fd8ab139892e
5
5
  SHA512:
6
- metadata.gz: 8a872f34699bfaa4f955c714c4e49eed52e87b009afc0a642d0ad434956f6a14690188ad9c359cc7aadd53e96fbd0d5d320e00aa263265e1ce3d3e369e8bd124
7
- data.tar.gz: 06444ea3e08b961445db6cb1a9093fc322b40ddf47daa00deb09a77f3b114014be8f525d7d391430b578b7916042bf345759d113c74e41a8ea51d65791abe69c
6
+ metadata.gz: 8d65637f892541a79fde71e3f523dfc37c887ec4892315ffe58b450718c8fa6e2a342036c394c28f5a94da0a1348948a104609881109c5621488b9d23a581bc1
7
+ data.tar.gz: 1d25dbac214c23c2af2bd991d4c57bdae9dce488ac886cc700d7ab10a7e3b4a0e72ddc2c1d4be459b332e9cdfac69588d42eac11ff78023d365e0d5ef6701f78
data/.rubocop.yml CHANGED
@@ -37,3 +37,4 @@ Sorbet/StrictSigil:
37
37
  Exclude:
38
38
  - "**/*.rake"
39
39
  - "test/**/*.rb"
40
+ - "lib/ruby-lsp.rb"
data/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.1]
10
+
11
+ - Implement the exit lifecycle request (https://github.com/Shopify/ruby-lsp/pull/198)
12
+ - Remove the Sorbet runtime from the gem's default load path (https://github.com/Shopify/ruby-lsp/pull/214)
13
+ - Return nil if the document is already formatted (https://github.com/Shopify/ruby-lsp/pull/216)
14
+ - Handle nameless keyword rest parameters in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/222)
15
+ - Display a warning on invalid RuboCop configuration (https://github.com/Shopify/ruby-lsp/pull/226)
16
+ - Centralize request handling logic in server.rb (https://github.com/Shopify/ruby-lsp/pull/221)
17
+ - Fix folding ranges for chained invocations involving an FCall (https://github.com/Shopify/ruby-lsp/pull/223)
18
+ - Fix handling of argument fowarding in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/228)
19
+ - Recover from initial syntax errors when opening documents (https://github.com/Shopify/ruby-lsp/pull/224)
20
+ - Highlight occurrences and definitions in document highlight (https://github.com/Shopify/ruby-lsp/pull/187)
21
+
9
22
  ## [0.2.0]
10
23
 
11
24
  - Add semantic token for keyword and keyword rest params (https://github.com/Shopify/ruby-lsp/pull/142)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-lsp (0.2.0)
4
+ ruby-lsp (0.2.1)
5
5
  language_server-protocol
6
6
  sorbet-runtime
7
7
  syntax_tree (>= 2.4)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
data/exe/ruby-lsp CHANGED
@@ -18,6 +18,4 @@ rescue
18
18
  nil
19
19
  end
20
20
 
21
- require_relative "../lib/ruby_lsp/internal"
22
-
23
- RubyLsp::Cli.start
21
+ require_relative "../lib/ruby_lsp/server"
data/lib/ruby-lsp.rb CHANGED
@@ -1,6 +1,6 @@
1
- # typed: strict
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
- VERSION = T.let(File.read(File.expand_path("../VERSION", __dir__)).strip, String)
5
+ VERSION = File.read(File.expand_path("../VERSION", __dir__)).strip
6
6
  end
@@ -9,7 +9,7 @@ module RubyLsp
9
9
  RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
10
10
  EditShape = T.type_alias { { range: RangeShape, text: String } }
11
11
 
12
- sig { returns(SyntaxTree::Node) }
12
+ sig { returns(T.nilable(SyntaxTree::Node)) }
13
13
  attr_reader :tree
14
14
 
15
15
  sig { returns(String) }
@@ -20,11 +20,13 @@ module RubyLsp
20
20
 
21
21
  sig { params(source: String).void }
22
22
  def initialize(source)
23
- @tree = T.let(SyntaxTree.parse(source), SyntaxTree::Node)
24
23
  @cache = T.let({}, T::Hash[Symbol, T.untyped])
25
24
  @syntax_error_edits = T.let([], T::Array[EditShape])
26
- @source = source
25
+ @source = T.let(source, String)
27
26
  @parsable_source = T.let(source.dup, String)
27
+ @tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
28
+ rescue SyntaxTree::Parser::ParseError
29
+ # Do not raise if we failed to parse
28
30
  end
29
31
 
30
32
  sig { params(other: Document).returns(T::Boolean) }
@@ -67,6 +69,11 @@ module RubyLsp
67
69
  @syntax_error_edits.any?
68
70
  end
69
71
 
72
+ sig { returns(T::Boolean) }
73
+ def parsed?
74
+ !@tree.nil?
75
+ end
76
+
70
77
  private
71
78
 
72
79
  sig { params(edits: T::Array[EditShape]).void }
@@ -6,17 +6,24 @@ require "ruby_lsp/store"
6
6
  require "benchmark"
7
7
 
8
8
  module RubyLsp
9
+ Interface = LanguageServer::Protocol::Interface
10
+ Constant = LanguageServer::Protocol::Constant
11
+ Transport = LanguageServer::Protocol::Transport
12
+
9
13
  class Handler
10
14
  extend T::Sig
11
15
  VOID = T.let(Object.new.freeze, Object)
12
16
 
17
+ sig { params(blk: T.proc.bind(Handler).params(arg0: T.untyped).void).void }
18
+ def self.start(&blk)
19
+ handler = new
20
+ handler.instance_exec(&blk)
21
+ handler.start
22
+ end
23
+
13
24
  sig { returns(Store) }
14
25
  attr_reader :store
15
26
 
16
- Interface = LanguageServer::Protocol::Interface
17
- Constant = LanguageServer::Protocol::Constant
18
- Transport = LanguageServer::Protocol::Transport
19
-
20
27
  sig { void }
21
28
  def initialize
22
29
  @writer = T.let(Transport::Stdio::Writer.new, Transport::Stdio::Writer)
@@ -31,11 +38,6 @@ module RubyLsp
31
38
  @reader.read { |request| handle(request) }
32
39
  end
33
40
 
34
- sig { params(blk: T.proc.bind(Handler).params(arg0: T.untyped).void).void }
35
- def config(&blk)
36
- instance_exec(&blk)
37
- end
38
-
39
41
  private
40
42
 
41
43
  sig do
@@ -84,109 +86,14 @@ module RubyLsp
84
86
  store.clear
85
87
  end
86
88
 
87
- sig { params(enabled_features: T::Array[String]).returns(Interface::InitializeResult) }
88
- def respond_with_capabilities(enabled_features)
89
- document_symbol_provider = if enabled_features.include?("documentSymbols")
90
- Interface::DocumentSymbolClientCapabilities.new(
91
- hierarchical_document_symbol_support: true,
92
- symbol_kind: {
93
- value_set: Requests::DocumentSymbol::SYMBOL_KIND.values,
94
- }
95
- )
96
- end
97
-
98
- folding_ranges_provider = if enabled_features.include?("foldingRanges")
99
- Interface::FoldingRangeClientCapabilities.new(line_folding_only: true)
100
- end
101
-
102
- semantic_tokens_provider = if enabled_features.include?("semanticHighlighting")
103
- Interface::SemanticTokensRegistrationOptions.new(
104
- document_selector: { scheme: "file", language: "ruby" },
105
- legend: Interface::SemanticTokensLegend.new(
106
- token_types: Requests::SemanticHighlighting::TOKEN_TYPES.keys,
107
- token_modifiers: Requests::SemanticHighlighting::TOKEN_MODIFIERS.keys
108
- ),
109
- range: false,
110
- full: {
111
- delta: true,
112
- }
113
- )
114
- end
115
-
116
- Interface::InitializeResult.new(
117
- capabilities: Interface::ServerCapabilities.new(
118
- text_document_sync: Interface::TextDocumentSyncOptions.new(
119
- change: Constant::TextDocumentSyncKind::INCREMENTAL,
120
- open_close: true,
121
- ),
122
- selection_range_provider: enabled_features.include?("selectionRanges"),
123
- document_symbol_provider: document_symbol_provider,
124
- folding_range_provider: folding_ranges_provider,
125
- semantic_tokens_provider: semantic_tokens_provider,
126
- document_formatting_provider: enabled_features.include?("formatting"),
127
- document_highlight_provider: enabled_features.include?("documentHighlights"),
128
- code_action_provider: enabled_features.include?("codeActions")
129
- )
130
- )
131
- end
132
-
133
- sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
134
- def respond_with_document_symbol(uri)
135
- store.cache_fetch(uri, :document_symbol) do |document|
136
- RubyLsp::Requests::DocumentSymbol.new(document).run
137
- end
138
- end
139
-
140
- sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::FoldingRange]) }
141
- def respond_with_folding_ranges(uri)
142
- store.cache_fetch(uri, :folding_ranges) do |document|
143
- Requests::FoldingRanges.new(document).run
144
- end
145
- end
146
-
147
- sig do
148
- params(
149
- uri: String,
150
- positions: T::Array[Document::PositionShape]
151
- ).returns(T::Array[T.nilable(RubyLsp::Requests::Support::SelectionRange)])
152
- end
153
- def respond_with_selection_ranges(uri, positions)
154
- ranges = store.cache_fetch(uri, :selection_ranges) do |document|
155
- Requests::SelectionRanges.new(document).run
156
- end
157
-
158
- # Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
159
- # every position in the positions array should have an element at the same index in the response
160
- # array. For positions without a valid selection range, the corresponding element in the response
161
- # array will be nil.
162
- positions.map do |position|
163
- ranges.find do |range|
164
- range.cover?(position)
165
- end
166
- end
167
- end
168
-
169
- sig { params(uri: String).returns(LanguageServer::Protocol::Interface::SemanticTokens) }
170
- def respond_with_semantic_highlighting(uri)
171
- store.cache_fetch(uri, :semantic_highlighting) do |document|
172
- T.cast(
173
- Requests::SemanticHighlighting.new(document, encoder: Requests::Support::SemanticTokenEncoder.new).run,
174
- LanguageServer::Protocol::Interface::SemanticTokens
175
- )
176
- end
177
- end
178
-
179
- sig { params(uri: String).returns(T.nilable(T::Array[LanguageServer::Protocol::Interface::TextEdit])) }
180
- def respond_with_formatting(uri)
181
- Requests::Formatting.new(uri, store.get(uri)).run
182
- end
183
-
184
89
  sig { params(uri: String).void }
185
90
  def send_diagnostics(uri)
186
91
  response = store.cache_fetch(uri, :diagnostics) do |document|
187
92
  Requests::Diagnostics.new(uri, document).run
188
93
  end
189
94
 
95
+ return if response.nil?
96
+
190
97
  @writer.write(
191
98
  method: "textDocument/publishDiagnostics",
192
99
  params: Interface::PublishDiagnosticsParams.new(
@@ -194,6 +101,8 @@ module RubyLsp
194
101
  diagnostics: response.map(&:to_lsp_diagnostic)
195
102
  )
196
103
  )
104
+ rescue RuboCop::ValidationError => e
105
+ show_message(Constant::MessageType::ERROR, "Error in RuboCop configuration file: #{e.message}")
197
106
  end
198
107
 
199
108
  sig { params(uri: String).void }
@@ -204,23 +113,12 @@ module RubyLsp
204
113
  )
205
114
  end
206
115
 
207
- sig do
208
- params(uri: String, range: T::Range[Integer]).returns(T::Array[LanguageServer::Protocol::Interface::CodeAction])
209
- end
210
- def respond_with_code_actions(uri, range)
211
- store.cache_fetch(uri, :code_actions) do |document|
212
- Requests::CodeActions.new(uri, document, range).run
213
- end
214
- end
215
-
216
- sig do
217
- params(
218
- uri: String,
219
- position: Document::PositionShape
220
- ).returns(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
221
- end
222
- def respond_with_document_highlight(uri, position)
223
- Requests::DocumentHighlight.new(store.get(uri), position).run
116
+ sig { params(type: Integer, message: String).void }
117
+ def show_message(type, message)
118
+ @writer.write(
119
+ method: "window/showMessage",
120
+ params: Interface::ShowMessageParams.new(type: type, message: message)
121
+ )
224
122
  end
225
123
 
226
124
  sig do
@@ -3,5 +3,7 @@
3
3
 
4
4
  require "sorbet-runtime"
5
5
  require "syntax_tree"
6
+ require "language_server-protocol"
7
+
6
8
  require "ruby-lsp"
7
- require "ruby_lsp/cli"
9
+ require "ruby_lsp/handler"
@@ -25,21 +25,11 @@ module RubyLsp
25
25
  class DocumentHighlight < BaseRequest
26
26
  extend T::Sig
27
27
 
28
- VarNodes = T.type_alias do
29
- T.any(
30
- SyntaxTree::GVar,
31
- SyntaxTree::Ident,
32
- SyntaxTree::IVar,
33
- SyntaxTree::Const,
34
- SyntaxTree::CVar
35
- )
36
- end
37
-
38
28
  sig { params(document: Document, position: Document::PositionShape).void }
39
29
  def initialize(document, position)
40
30
  @highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
41
31
  position = Document::Scanner.new(document.source).find_position(position)
42
- @target = T.let(find(document.tree, position), T.nilable(VarNodes))
32
+ @target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
43
33
 
44
34
  super(document)
45
35
  end
@@ -53,33 +43,24 @@ module RubyLsp
53
43
  @highlights
54
44
  end
55
45
 
56
- sig { params(node: SyntaxTree::VarField).void }
57
- def visit_var_field(node)
58
- if matches_target?(node.value)
59
- add_highlight(
60
- node.value,
61
- LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
62
- )
63
- end
64
-
65
- super
66
- end
46
+ sig { params(node: T.nilable(SyntaxTree::Node)).void }
47
+ def visit(node)
48
+ return if node.nil?
67
49
 
68
- sig { params(node: SyntaxTree::VarRef).void }
69
- def visit_var_ref(node)
70
- if matches_target?(node.value)
71
- add_highlight(
72
- node.value,
73
- LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
74
- )
75
- end
50
+ match = T.must(@target).highlight_type(node)
51
+ add_highlight(match) if match
76
52
 
77
53
  super
78
54
  end
79
55
 
80
56
  private
81
57
 
82
- sig { params(node: SyntaxTree::Node, position: Integer).returns(T.nilable(VarNodes)) }
58
+ sig do
59
+ params(
60
+ node: SyntaxTree::Node,
61
+ position: Integer,
62
+ ).returns(T.nilable(Support::HighlightTarget))
63
+ end
83
64
  def find(node, position)
84
65
  matched =
85
66
  node.child_nodes.compact.bsearch do |child|
@@ -91,22 +72,24 @@ module RubyLsp
91
72
  end
92
73
 
93
74
  case matched
94
- when SyntaxTree::GVar, SyntaxTree::Ident, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar
95
- matched
75
+ when SyntaxTree::GVar,
76
+ SyntaxTree::IVar,
77
+ SyntaxTree::Const,
78
+ SyntaxTree::CVar,
79
+ SyntaxTree::VarField
80
+ Support::HighlightTarget.new(matched)
81
+ when SyntaxTree::Ident
82
+ relevant_node = node.is_a?(SyntaxTree::Params) ? matched : node
83
+ Support::HighlightTarget.new(relevant_node)
96
84
  when SyntaxTree::Node
97
85
  find(matched, position)
98
86
  end
99
87
  end
100
88
 
101
- sig { params(node: SyntaxTree::Node).returns(T::Boolean) }
102
- def matches_target?(node)
103
- node.is_a?(@target.class) && T.cast(node, VarNodes).value == T.must(@target).value
104
- end
105
-
106
- sig { params(node: SyntaxTree::Node, kind: Integer).void }
107
- def add_highlight(node, kind)
108
- range = range_from_syntax_tree_node(node)
109
- @highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: kind)
89
+ sig { params(match: Support::HighlightTarget::HighlightMatch).void }
90
+ def add_highlight(match)
91
+ range = range_from_syntax_tree_node(match.node)
92
+ @highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: match.type)
110
93
  end
111
94
  end
112
95
  end
@@ -195,7 +195,11 @@ module RubyLsp
195
195
  receiver = receiver.receiver
196
196
  when SyntaxTree::MethodAddBlock
197
197
  visit(receiver.block)
198
- receiver = receiver.call.receiver
198
+ receiver = receiver.call
199
+
200
+ if receiver.is_a?(SyntaxTree::Call) || receiver.is_a?(SyntaxTree::CommandCall)
201
+ receiver = receiver.receiver
202
+ end
199
203
  else
200
204
  break
201
205
  end
@@ -34,6 +34,7 @@ module RubyLsp
34
34
  return unless formatted_text
35
35
 
36
36
  size = @document.source.size
37
+ return if formatted_text.size == size && formatted_text == @document.source
37
38
 
38
39
  [
39
40
  LanguageServer::Protocol::Interface::TextEdit.new(
@@ -78,7 +78,7 @@ module RubyLsp
78
78
 
79
79
  @encoder = encoder
80
80
  @tokens = T.let([], T::Array[SemanticToken])
81
- @tree = T.let(document.tree, SyntaxTree::Node)
81
+ @tree = T.let(T.must(document.tree), SyntaxTree::Node)
82
82
  @special_methods = T.let(nil, T.nilable(T::Array[String]))
83
83
  end
84
84
 
@@ -179,7 +179,11 @@ module RubyLsp
179
179
  add_token(location_without_colon(location), :variable)
180
180
  end
181
181
 
182
- add_token(node.keyword_rest.name.location, :variable) if node.keyword_rest
182
+ rest = node.keyword_rest
183
+ return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
184
+
185
+ name = rest.name
186
+ add_token(name.location, :variable) if name
183
187
  end
184
188
 
185
189
  sig { params(node: SyntaxTree::VarField).void }
@@ -0,0 +1,87 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ module Support
7
+ class HighlightTarget
8
+ extend T::Sig
9
+
10
+ READ = LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
11
+ WRITE = LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
12
+
13
+ class HighlightMatch < T::Struct
14
+ const :type, Integer
15
+ const :node, SyntaxTree::Node
16
+ end
17
+
18
+ sig { params(node: SyntaxTree::Node).void }
19
+ def initialize(node)
20
+ @node = node
21
+ @value = T.let(value(node), T.nilable(String))
22
+ end
23
+
24
+ sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
25
+ def highlight_type(other)
26
+ matched_highlight(other) if other.is_a?(SyntaxTree::Params) || (@value && @value == value(other))
27
+ end
28
+
29
+ private
30
+
31
+ # Match the target type (where the cursor is positioned) with the `other` type (the node we're currently
32
+ # visiting)
33
+ sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
34
+ def matched_highlight(other)
35
+ case @node
36
+ # Method definitions and invocations
37
+ when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command,
38
+ SyntaxTree::CommandCall, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless
39
+ case other
40
+ when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
41
+ HighlightMatch.new(type: READ, node: other)
42
+ when SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::Defs
43
+ HighlightMatch.new(type: WRITE, node: other.name)
44
+ end
45
+ # Variables, parameters and constants
46
+ when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::VarField,
47
+ SyntaxTree::VarRef, SyntaxTree::Ident
48
+ case other
49
+ when SyntaxTree::VarField
50
+ HighlightMatch.new(type: WRITE, node: other)
51
+ when SyntaxTree::VarRef
52
+ HighlightMatch.new(type: READ, node: other)
53
+ when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
54
+ HighlightMatch.new(type: WRITE, node: other.constant)
55
+ when SyntaxTree::ConstPathRef
56
+ HighlightMatch.new(type: READ, node: other.constant)
57
+ when SyntaxTree::Params
58
+ params = other.child_nodes.compact
59
+ match = params.find { |param| value(param) == @value }
60
+ HighlightMatch.new(type: WRITE, node: match) if match
61
+ end
62
+ end
63
+ end
64
+
65
+ sig { params(node: SyntaxTree::Node).returns(T.nilable(String)) }
66
+ def value(node)
67
+ case node
68
+ when SyntaxTree::ConstPathRef, SyntaxTree::ConstPathField, SyntaxTree::TopConstField
69
+ node.constant.value
70
+ when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::Ident,
71
+ SyntaxTree::ArgsForward
72
+ node.value
73
+ when SyntaxTree::Field, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless, SyntaxTree::RestParam,
74
+ SyntaxTree::KwRestParam, SyntaxTree::BlockArg
75
+ node.name&.value
76
+ when SyntaxTree::VarField, SyntaxTree::VarRef, SyntaxTree::VCall, SyntaxTree::FCall
77
+ node.value&.value
78
+ when SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
79
+ node.message.value
80
+ when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
81
+ node.constant.constant.value
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -29,6 +29,7 @@ module RubyLsp
29
29
  autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
30
30
  autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
31
31
  autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
32
+ autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
32
33
  end
33
34
  end
34
35
  end
@@ -0,0 +1,160 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "ruby_lsp/internal"
5
+
6
+ module RubyLsp
7
+ Handler.start do
8
+ on("initialize") do |request|
9
+ store.clear
10
+ initialization_options = request.dig(:params, :initializationOptions)
11
+ enabled_features = initialization_options.fetch(:enabledFeatures, [])
12
+
13
+ document_symbol_provider = if enabled_features.include?("documentSymbols")
14
+ Interface::DocumentSymbolClientCapabilities.new(
15
+ hierarchical_document_symbol_support: true,
16
+ symbol_kind: {
17
+ value_set: Requests::DocumentSymbol::SYMBOL_KIND.values,
18
+ }
19
+ )
20
+ end
21
+
22
+ folding_ranges_provider = if enabled_features.include?("foldingRanges")
23
+ Interface::FoldingRangeClientCapabilities.new(line_folding_only: true)
24
+ end
25
+
26
+ semantic_tokens_provider = if enabled_features.include?("semanticHighlighting")
27
+ Interface::SemanticTokensRegistrationOptions.new(
28
+ document_selector: { scheme: "file", language: "ruby" },
29
+ legend: Interface::SemanticTokensLegend.new(
30
+ token_types: Requests::SemanticHighlighting::TOKEN_TYPES.keys,
31
+ token_modifiers: Requests::SemanticHighlighting::TOKEN_MODIFIERS.keys
32
+ ),
33
+ range: false,
34
+ full: {
35
+ delta: true,
36
+ }
37
+ )
38
+ end
39
+
40
+ Interface::InitializeResult.new(
41
+ capabilities: Interface::ServerCapabilities.new(
42
+ text_document_sync: Interface::TextDocumentSyncOptions.new(
43
+ change: Constant::TextDocumentSyncKind::INCREMENTAL,
44
+ open_close: true,
45
+ ),
46
+ selection_range_provider: enabled_features.include?("selectionRanges"),
47
+ document_symbol_provider: document_symbol_provider,
48
+ folding_range_provider: folding_ranges_provider,
49
+ semantic_tokens_provider: semantic_tokens_provider,
50
+ document_formatting_provider: enabled_features.include?("formatting"),
51
+ document_highlight_provider: enabled_features.include?("documentHighlights"),
52
+ code_action_provider: enabled_features.include?("codeActions")
53
+ )
54
+ )
55
+ end
56
+
57
+ on("textDocument/didChange") do |request|
58
+ uri = request.dig(:params, :textDocument, :uri)
59
+ store.push_edits(uri, request.dig(:params, :contentChanges))
60
+
61
+ send_diagnostics(uri)
62
+ Handler::VOID
63
+ end
64
+
65
+ on("textDocument/didOpen") do |request|
66
+ uri = request.dig(:params, :textDocument, :uri)
67
+ text = request.dig(:params, :textDocument, :text)
68
+ store.set(uri, text)
69
+
70
+ send_diagnostics(uri)
71
+ Handler::VOID
72
+ end
73
+
74
+ on("textDocument/didClose") do |request|
75
+ uri = request.dig(:params, :textDocument, :uri)
76
+ store.delete(uri)
77
+ clear_diagnostics(uri)
78
+
79
+ Handler::VOID
80
+ end
81
+
82
+ on("textDocument/documentSymbol") do |request|
83
+ store.cache_fetch(request.dig(:params, :textDocument, :uri), :document_symbol) do |document|
84
+ Requests::DocumentSymbol.new(document).run
85
+ end
86
+ end
87
+
88
+ on("textDocument/foldingRange") do |request|
89
+ store.cache_fetch(request.dig(:params, :textDocument, :uri), :folding_ranges) do |document|
90
+ Requests::FoldingRanges.new(document).run
91
+ end
92
+ end
93
+
94
+ on("textDocument/selectionRange") do |request|
95
+ uri = request.dig(:params, :textDocument, :uri)
96
+ positions = request.dig(:params, :positions)
97
+
98
+ ranges = store.cache_fetch(uri, :selection_ranges) do |document|
99
+ Requests::SelectionRanges.new(document).run
100
+ end
101
+
102
+ return if ranges.nil?
103
+
104
+ # Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
105
+ # every position in the positions array should have an element at the same index in the response
106
+ # array. For positions without a valid selection range, the corresponding element in the response
107
+ # array will be nil.
108
+ positions.map do |position|
109
+ ranges.find do |range|
110
+ range.cover?(position)
111
+ end
112
+ end
113
+ end
114
+
115
+ on("textDocument/semanticTokens/full") do |request|
116
+ store.cache_fetch(request.dig(:params, :textDocument, :uri), :semantic_highlighting) do |document|
117
+ T.cast(
118
+ Requests::SemanticHighlighting.new(
119
+ document,
120
+ encoder: Requests::Support::SemanticTokenEncoder.new
121
+ ).run,
122
+ LanguageServer::Protocol::Interface::SemanticTokens
123
+ )
124
+ end
125
+ end
126
+
127
+ on("textDocument/formatting") do |request|
128
+ uri = request.dig(:params, :textDocument, :uri)
129
+
130
+ Requests::Formatting.new(uri, store.get(uri)).run
131
+ end
132
+
133
+ on("textDocument/documentHighlight") do |request|
134
+ document = store.get(request.dig(:params, :textDocument, :uri))
135
+ return unless document.parsed?
136
+
137
+ Requests::DocumentHighlight.new(document, request.dig(:params, :position)).run
138
+ end
139
+
140
+ on("textDocument/codeAction") do |request|
141
+ uri = request.dig(:params, :textDocument, :uri)
142
+ range = request.dig(:params, :range)
143
+ start_line = range.dig(:start, :line)
144
+ end_line = range.dig(:end, :line)
145
+
146
+ store.cache_fetch(uri, :code_actions) do |document|
147
+ Requests::CodeActions.new(uri, document, start_line..end_line).run
148
+ end
149
+ end
150
+
151
+ on("shutdown") { shutdown }
152
+
153
+ on("exit") do
154
+ # We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
155
+ # https://microsoft.github.io/language-server-protocol/specification/#exit
156
+ status = store.empty? ? 0 : 1
157
+ exit(status)
158
+ end
159
+ end
160
+ end
@@ -25,9 +25,8 @@ module RubyLsp
25
25
 
26
26
  sig { params(uri: String, content: String).void }
27
27
  def set(uri, content)
28
- @state[uri] = Document.new(content)
29
- rescue SyntaxTree::Parser::ParseError
30
- # Do not update the store if there are syntax errors
28
+ document = Document.new(content)
29
+ @state[uri] = document
31
30
  end
32
31
 
33
32
  sig { params(uri: String, edits: T::Array[Document::EditShape]).void }
@@ -40,6 +39,11 @@ module RubyLsp
40
39
  @state.clear
41
40
  end
42
41
 
42
+ sig { returns(T::Boolean) }
43
+ def empty?
44
+ @state.empty?
45
+ end
46
+
43
47
  sig { params(uri: String).void }
44
48
  def delete(uri)
45
49
  @state.delete(uri)
@@ -51,10 +55,13 @@ module RubyLsp
51
55
  uri: String,
52
56
  request_name: Symbol,
53
57
  block: T.proc.params(document: Document).returns(T.type_parameter(:T))
54
- ).returns(T.type_parameter(:T))
58
+ ).returns(T.nilable(T.type_parameter(:T)))
55
59
  end
56
60
  def cache_fetch(uri, request_name, &block)
57
- get(uri).cache_fetch(request_name, &block)
61
+ document = get(uri)
62
+ return unless document.parsed?
63
+
64
+ document.cache_fetch(request_name, &block)
58
65
  end
59
66
  end
60
67
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-07 00:00:00.000000000 Z
11
+ date: 2022-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -85,7 +85,6 @@ files:
85
85
  - dev.yml
86
86
  - exe/ruby-lsp
87
87
  - lib/ruby-lsp.rb
88
- - lib/ruby_lsp/cli.rb
89
88
  - lib/ruby_lsp/document.rb
90
89
  - lib/ruby_lsp/handler.rb
91
90
  - lib/ruby_lsp/internal.rb
@@ -99,12 +98,14 @@ files:
99
98
  - lib/ruby_lsp/requests/formatting.rb
100
99
  - lib/ruby_lsp/requests/selection_ranges.rb
101
100
  - lib/ruby_lsp/requests/semantic_highlighting.rb
101
+ - lib/ruby_lsp/requests/support/highlight_target.rb
102
102
  - lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
103
103
  - lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb
104
104
  - lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
105
105
  - lib/ruby_lsp/requests/support/selection_range.rb
106
106
  - lib/ruby_lsp/requests/support/semantic_token_encoder.rb
107
107
  - lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb
108
+ - lib/ruby_lsp/server.rb
108
109
  - lib/ruby_lsp/store.rb
109
110
  - rakelib/check_docs.rake
110
111
  - ruby-lsp.gemspec
data/lib/ruby_lsp/cli.rb DELETED
@@ -1,89 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "language_server-protocol"
5
-
6
- require_relative "handler"
7
-
8
- module RubyLsp
9
- module Cli
10
- extend T::Sig
11
-
12
- sig { void }
13
- def self.start
14
- handler = RubyLsp::Handler.new
15
-
16
- handler.config do
17
- on("initialize") do |request|
18
- store.clear
19
- initialization_options = request.dig(:params, :initializationOptions)
20
-
21
- respond_with_capabilities(initialization_options.fetch(:enabledFeatures, []))
22
- end
23
-
24
- on("textDocument/didChange") do |request|
25
- uri = request.dig(:params, :textDocument, :uri)
26
- store.push_edits(uri, request.dig(:params, :contentChanges))
27
-
28
- send_diagnostics(uri)
29
- RubyLsp::Handler::VOID
30
- end
31
-
32
- on("textDocument/didOpen") do |request|
33
- uri = request.dig(:params, :textDocument, :uri)
34
- text = request.dig(:params, :textDocument, :text)
35
- store.set(uri, text)
36
-
37
- send_diagnostics(uri)
38
- RubyLsp::Handler::VOID
39
- end
40
-
41
- on("textDocument/didClose") do |request|
42
- uri = request.dig(:params, :textDocument, :uri)
43
- store.delete(uri)
44
- clear_diagnostics(uri)
45
-
46
- RubyLsp::Handler::VOID
47
- end
48
-
49
- on("textDocument/documentSymbol") do |request|
50
- respond_with_document_symbol(request.dig(:params, :textDocument, :uri))
51
- end
52
-
53
- on("textDocument/foldingRange") do |request|
54
- respond_with_folding_ranges(request.dig(:params, :textDocument, :uri))
55
- end
56
-
57
- on("textDocument/selectionRange") do |request|
58
- respond_with_selection_ranges(
59
- request.dig(:params, :textDocument, :uri),
60
- request.dig(:params, :positions),
61
- )
62
- end
63
-
64
- on("textDocument/semanticTokens/full") do |request|
65
- respond_with_semantic_highlighting(request.dig(:params, :textDocument, :uri))
66
- end
67
-
68
- on("textDocument/formatting") do |request|
69
- respond_with_formatting(request.dig(:params, :textDocument, :uri))
70
- end
71
-
72
- on("textDocument/documentHighlight") do |request|
73
- respond_with_document_highlight(request.dig(:params, :textDocument, :uri), request.dig(:params, :position))
74
- end
75
-
76
- on("textDocument/codeAction") do |request|
77
- range = request.dig(:params, :range)
78
- start_line = range.dig(:start, :line)
79
- end_line = range.dig(:end, :line)
80
- respond_with_code_actions(request.dig(:params, :textDocument, :uri), (start_line..end_line))
81
- end
82
-
83
- on("shutdown") { shutdown }
84
- end
85
-
86
- handler.start
87
- end
88
- end
89
- end