ruby-lsp 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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