-
Notifications
You must be signed in to change notification settings - Fork 155
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
Fix the handling of multibyte characters #2051
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. These changes look style related only. Can we remove them to make it easier to review? |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,19 +18,19 @@ def perform; end | |
private | ||
|
||
# Checks if a location covers a position | ||
sig { params(location: Prism::Location, position: T.untyped).returns(T::Boolean) } | ||
def cover?(location, position) | ||
sig { params(location: Prism::Location, position: T.untyped, encoding: Encoding).returns(T::Boolean) } | ||
def cover?(location, position, encoding) | ||
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. Could we pass the encoding to the 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. Thanks for your quickly review. However, if it’s global_state, it might be okay since it’s already used by multiple requests. Alternatively, we can pass the global_state to the request initializer instead of encoding. If it is global_state, it wouldn't be odd to maintain the state within the request, in my opinion. What do you think? 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. 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. @andyw8 @vinistock |
||
start_covered = | ||
location.start_line - 1 < position[:line] || | ||
( | ||
location.start_line - 1 == position[:line] && | ||
location.start_column <= position[:character] | ||
location.start_code_units_column(encoding) <= position[:character] | ||
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. Same question as above here, could we convert |
||
) | ||
end_covered = | ||
location.end_line - 1 > position[:line] || | ||
( | ||
location.end_line - 1 == position[:line] && | ||
location.end_column >= position[:character] | ||
location.end_code_units_column(encoding) >= position[:character] | ||
) | ||
start_covered && end_covered | ||
end | ||
|
@@ -50,15 +50,16 @@ def cover?(location, position) | |
target: Prism::Node, | ||
parent: Prism::Node, | ||
position: T::Hash[Symbol, Integer], | ||
encoding: Encoding, | ||
).returns(Prism::Node) | ||
end | ||
def determine_target(target, parent, position) | ||
def determine_target(target, parent, position, encoding) | ||
return target unless parent.is_a?(Prism::ConstantPathNode) | ||
|
||
target = T.let(parent, Prism::Node) | ||
parent = T.let(T.cast(target, Prism::ConstantPathNode).parent, T.nilable(Prism::Node)) | ||
|
||
while parent && cover?(parent.location, position) | ||
while parent && cover?(parent.location, position, encoding) | ||
target = parent | ||
parent = target.is_a?(Prism::ConstantPathNode) ? target.parent : nil | ||
end | ||
|
@@ -67,17 +68,23 @@ def determine_target(target, parent, position) | |
end | ||
|
||
# Checks if a given location covers the position requested | ||
sig { params(location: T.nilable(Prism::Location), position: T::Hash[Symbol, T.untyped]).returns(T::Boolean) } | ||
def covers_position?(location, position) | ||
sig do | ||
params( | ||
location: T.nilable(Prism::Location), | ||
position: T::Hash[Symbol, T.untyped], | ||
encoding: Encoding, | ||
).returns(T::Boolean) | ||
end | ||
def covers_position?(location, position, encoding) | ||
return false unless location | ||
|
||
start_line = location.start_line - 1 | ||
end_line = location.end_line - 1 | ||
line = position[:line] | ||
character = position[:character] | ||
|
||
(start_line < line || (start_line == line && location.start_column <= character)) && | ||
(end_line > line || (end_line == line && location.end_column >= character)) | ||
(start_line < line || (start_line == line && location.start_code_units_column(encoding) <= character)) && | ||
(end_line > line || (end_line == line && location.end_code_units_column(encoding) >= character)) | ||
end | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"result": [ | ||
{ | ||
"uri": "file:///fixtures/multibyte_characters.rb", | ||
"range": { | ||
"start": { | ||
"line": 2, | ||
"character": 2 | ||
}, | ||
"end": { | ||
"line": 8, | ||
"character": 5 | ||
} | ||
} | ||
} | ||
], | ||
"params": [ | ||
{ | ||
"line": 1, | ||
"character": 7 | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# テスト | ||
A動物::C貓咪.叫 |
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.
start_offset
andend_offset
are a lot less costly to calculate thanstart_code_units_offset
andend_code_units_offset
. Could we instead convertchar_position
into a byte offset and keep the existing code the same?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.
Thanks for your comments.
In my understanding, the char_position is provided by the editor, so we can't convert it into a byte offset :(
Alternatively, we can memorize the loc.start_code_units_offset and others to reduce the calculation.
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.
We have the variable though, so we can convert it using our own transformation right? I think the general transformation would be:
source.slice(0, code_units_offset).length
code_units_offset / 2
code_units_offset / 4
I'm not 100% sure, but I think that's it.
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.
@kddnewton
Apologies for the delayed response. I have fixed the issue to convert byte offsets in this commit: be648a6. Please review it.