-
Notifications
You must be signed in to change notification settings - Fork 121
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
Provide constant type differentiation in semantic highlighting request #1324
Changes from all commits
60c0549
6b92772
cb206a7
87439be
d79ec2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ class SemanticHighlighting < Listener | |
regexp: 20, | ||
operator: 21, | ||
decorator: 22, | ||
constant: 23 | ||
}.freeze, | ||
T::Hash[Symbol, Integer], | ||
) | ||
|
@@ -92,26 +93,36 @@ def initialize(location:, length:, type:, modifier:) | |
sig { override.returns(ResponseType) } | ||
attr_reader :_response | ||
|
||
sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void } | ||
def initialize(dispatcher, range: nil) | ||
sig { | ||
params( | ||
dispatcher: Prism::Dispatcher, | ||
index: RubyIndexer::Index, | ||
range: T.nilable(T::Range[Integer]) | ||
).void | ||
} | ||
def initialize(dispatcher, index, range: nil) | ||
super(dispatcher) | ||
|
||
@_response = T.let([], ResponseType) | ||
@range = range | ||
@special_methods = T.let(nil, T.nilable(T::Array[String])) | ||
@current_scope = T.let(ParameterScope.new, ParameterScope) | ||
@index = index | ||
@nesting = T.let([], T::Array[String]) | ||
@inside_regex_capture = T.let(false, T::Boolean) | ||
|
||
dispatcher.register( | ||
self, | ||
:on_call_node_enter, | ||
:on_class_node_enter, | ||
:on_class_node_leave, | ||
:on_def_node_enter, | ||
:on_def_node_leave, | ||
:on_block_node_enter, | ||
:on_block_node_leave, | ||
:on_self_node_enter, | ||
:on_module_node_enter, | ||
:on_module_node_leave, | ||
:on_local_variable_write_node_enter, | ||
:on_local_variable_read_node_enter, | ||
:on_block_parameter_node_enter, | ||
|
@@ -171,9 +182,25 @@ def on_match_write_node_leave(node) | |
|
||
sig { params(node: Prism::ConstantReadNode).void } | ||
def on_constant_read_node_enter(node) | ||
return unless visible?(node, @range) | ||
entries = @index.resolve(node.name, @nesting) | ||
# if entries is nil, we didn't find the declaration for it | ||
# it might be defined with meta-programming or using C code | ||
unless entries | ||
add_token(node.location, :constant) | ||
end | ||
|
||
add_token(node.location, :namespace) | ||
# Otherwise, we can check the type of entry to determine the | ||
# type of constant | ||
first_entry = T.must(entries.first) | ||
|
||
case first_entry | ||
when RubyIndexer::Entry::Class | ||
add_token(node.location, :class, [:declaration]) | ||
when RubyIndexer::Entry::Module | ||
add_token(node.location, :namespace, [:declaration]) | ||
when RubyIndexer::Entry::Constant | ||
add_token(node.location, :constant, [:declaration]) | ||
Comment on lines
+198
to
+202
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Constant reads are references to constants, not declarations. So you need to remove the modifiers on all of these |
||
end | ||
end | ||
|
||
sig { params(node: Prism::ConstantWriteNode).void } | ||
|
@@ -366,19 +393,31 @@ def on_local_variable_target_node_enter(node) | |
def on_class_node_enter(node) | ||
return unless visible?(node, @range) | ||
|
||
@nesting.push(node.name) | ||
add_token(node.constant_path.location, :class, [:declaration]) | ||
|
||
superclass = node.superclass | ||
add_token(superclass.location, :class) if superclass | ||
end | ||
|
||
sig { params(node: Prism::ClassNode).void } | ||
def on_class_node_leave(node) | ||
@nesting.pop | ||
end | ||
|
||
sig { params(node: Prism::ModuleNode).void } | ||
def on_module_node_enter(node) | ||
return unless visible?(node, @range) | ||
|
||
@nesting.push(node.name) | ||
add_token(node.constant_path.location, :namespace, [:declaration]) | ||
end | ||
|
||
sig { params(node: Prism::ClassNode).void } | ||
def on_module_node_leave(node) | ||
@nesting.pop | ||
end | ||
|
||
private | ||
|
||
sig { params(location: Prism::Location, type: Symbol, modifiers: T::Array[Symbol]).void } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's not commit these changes. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,14 +11,34 @@ def run_expectations(source) | |
document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: URI("file:///fake.rb")) | ||
range = @__params&.any? ? @__params.first : nil | ||
|
||
store = RubyLsp::Store.new | ||
store.set(uri: URI("file:///folder/fake.rb"), source: source, version: 1) | ||
executor = RubyLsp::Executor.new(store, message_queue) | ||
index = executor.instance_variable_get(:@index) | ||
Dir.glob(TEST_RUBY_LSP_FIXTURES).each do |path| | ||
index.index_single( | ||
RubyIndexer::IndexablePath.new( | ||
"#{Dir.pwd}/lib", | ||
File.expand_path( | ||
"../../#{path}", | ||
__dir__, | ||
), | ||
), | ||
) | ||
end | ||
Comment on lines
+18
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of indexing every fixture here, we should use the index.index_single(
RubyIndexer::IndexablePath.new(
"fake",
"fake/path.rb",
),
source
) Otherwise, we'd be indexing every fixture file in the codebase every time we're running the test for a single fixture. |
||
|
||
if range | ||
start_line = range.dig(:start, :line) | ||
end_line = range.dig(:end, :line) | ||
processed_range = start_line..end_line | ||
end | ||
|
||
dispatcher = Prism::Dispatcher.new | ||
listener = RubyLsp::Requests::SemanticHighlighting.new(dispatcher, range: processed_range) | ||
listener = RubyLsp::Requests::SemanticHighlighting.new( | ||
dispatcher, | ||
executor.instance_variable_get(:@index), | ||
range: processed_range | ||
) | ||
|
||
dispatcher.dispatch(document.tree) | ||
RubyLsp::Requests::Support::SemanticTokenEncoder.new.encode(listener.perform) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This token type doesn't exist in the specification. We can't add it here because editors won't know what to do with that type.
We have two options here. We can use
static variable
as the token type for constants or we can propose the addition ofconstant
as a token type to the specification.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. I was completely unaware of this.
Particularly, I'll just use the
static variable
, but do you think there is value in adding theconstant
token type to the spec? I'm assuming this might be an issue that the spec is built thinking of wording that doesn't exactly match Ruby concepts. So maybestatic variable
would be the equivalent of a Rubyconstant
. Despite the glaring contradiction in naming.