Skip to content

Commit

Permalink
LSP: Refactor definition to be similar to hover feature
Browse files Browse the repository at this point in the history
  • Loading branch information
jansul committed May 6, 2023
1 parent e885542 commit d699a6b
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 133 deletions.
109 changes: 34 additions & 75 deletions src/ls/definition.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,84 +15,26 @@ module Mint
stack =
server.nodes_at_cursor(params)

html_style(server, workspace, stack) ||
html_attribute(server, workspace, stack) ||
html_component(server, workspace, stack)
if node = stack[0]?
definition(node, server, workspace, stack)
end
end
end

def with_stack(stack : Array(Ast::Node), &)
yield StackReader.new(stack)
def definition(node : Ast::Node, server : Server, workspace : Workspace, stack : Array(Ast::Node))
nil
end

def selection(location : Ast::Node::Location) : LSP::Range
LSP::Range.new(
start: LSP::Position.new(
line: location.start[0] - 1,
character: location.start[1]
),
end: LSP::Position.new(
line: location.end[0] - 1,
character: location.end[1]
)
)
end

# Returns the range for the name part for a node
def selection(node : Ast::Node) : LSP::Range
selection(node.location)
end

def selection(node : Ast::Component) : LSP::Range
# Select only the name part of the component
# global component MintComponent {
# ^^^^^^^^^^^^^
selection(node.name)
end

def selection(node : Ast::HtmlAttribute) : LSP::Range
# Select only the name part of the attribute
# <Component attribute={value}>
# ^^^^^^^^^
selection(node.name)
end

def selection(node : Ast::HtmlComponent) : LSP::Range
# Select only the name part of the component
# <Component attribute={value}>
# ^^^^^^^^^
selection(node.component)
def cursor_intersects?(node : Ast::Node, position : LSP::Position) : Bool
node.location.contains?(position.line + 1, position.character)
end

def selection(node : Ast::HtmlStyle) : LSP::Range
# Select only the name part of the component
# <div::style>
# ^^^^^

start_line, start_column = node.location.start

# Skip the first two characters "::"
location = Ast::Node::Location.new(
filename: node.location.filename,
start: {start_line, start_column + 2},
end: node.location.end
)

selection(location)
def cursor_intersects?(node : Ast::Node, params : LSP::TextDocumentPositionParams) : Bool
cursor_intersects?(node, params.position)
end

def selection(node : Ast::Property) : LSP::Range
# Select only the name part of the property
# property size : String = "small"
# ^^^^
selection(node.name)
end

def selection(node : Ast::Style) : LSP::Range
# Select only the name part of the style
# style app {
# ^^^
selection(node.name)
def cursor_intersects?(node : Ast::Node) : Bool
cursor_intersects?(node, params)
end

def find_component(workspace : Workspace, name : String) : Ast::Component?
Expand All @@ -106,19 +48,36 @@ module Mint
!!(server.params.try &.capabilities.try &.text_document.try &.definition.try &.link_support)
end

def to_lsp_range(location : Ast::Node::Location) : LSP::Range
LSP::Range.new(
start: LSP::Position.new(
line: location.start[0] - 1,
character: location.start[1]
),
end: LSP::Position.new(
line: location.end[0] - 1,
character: location.end[1]
)
)
end

# Returns a `LSP::LocationLink` that links from *source* to the *target* node
# if the *server* has link support, otherwise it returns `LSP::Location`.
def location_link(server : Server, source : Ast::Node, target : Ast::Node) : LSP::LocationLink | LSP::Location
# if the language server client has link support, otherwise it returns `LSP::Location`.
#
# When returning a `LSP::LocationLink`, *parent* is used to provide the full range
# for the *target* node. For example, for a function, *target* would be the function name,
# and *parent* would be the whole node, including function body and any comments
def location_link(server : Server, source : Ast::Node, target : Ast::Node, parent : Ast::Node) : LSP::LocationLink | LSP::Location
if has_link_support?(server)
LSP::LocationLink.new(
origin_selection_range: selection(source),
origin_selection_range: to_lsp_range(source.location),
target_uri: "file://#{target.location.filename}",
target_range: selection(target.location),
target_selection_range: selection(target)
target_range: to_lsp_range(parent.location),
target_selection_range: to_lsp_range(target.location)
)
else
LSP::Location.new(
range: selection(target),
range: to_lsp_range(target.location),
uri: "file://#{target.location.filename}",
)
end
Expand Down
20 changes: 8 additions & 12 deletions src/ls/definition/html_attribute.cr
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
module Mint
module LS
class Definition < LSP::RequestMessage
def html_attribute(server : Server, workspace : Workspace, stack : Array(Ast::Node))
with_stack(stack) do |reader|
return unless variable = reader.find_next Ast::Variable
def definition(node : Ast::HtmlAttribute, server : Server, workspace : Workspace, stack : Array(Ast::Node))
return unless cursor_intersects?(node.name)

return unless reader.find_next Ast::HtmlAttribute
return unless html_component = stack.find(&.is_a?(Ast::HtmlComponent)).as?(Ast::HtmlComponent)

return unless html_component = reader.find_next Ast::HtmlComponent
return unless component =
find_component(workspace, html_component.component.value)

return unless component =
find_component(workspace, html_component.component.value)
return unless component_property =
component.properties.find(&.name.value.== node.name.value)

return unless component_property =
component.properties.find(&.name.value.== variable.value)

location_link server, variable, component_property
end
location_link server, node.name, component_property.name, component_property
end
end
end
Expand Down
14 changes: 5 additions & 9 deletions src/ls/definition/html_component.cr
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
module Mint
module LS
class Definition < LSP::RequestMessage
def html_component(server : Server, workspace : Workspace, stack : Array(Ast::Node))
with_stack(stack) do |reader|
return unless type_id = reader.find_next Ast::TypeId
def definition(node : Ast::HtmlComponent, server : Server, workspace : Workspace, stack : Array(Ast::Node))
return unless cursor_intersects?(node.component)

return unless html_component = reader.find_next Ast::HtmlComponent
return unless component =
find_component(workspace, node.component.value)

return unless component =
find_component(workspace, html_component.component.value)

location_link server, type_id, component
end
location_link server, node.component, component.name, component
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions src/ls/definition/html_element.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Mint
module LS
class Definition < LSP::RequestMessage
def definition(node : Ast::HtmlElement, server : Server, workspace : Workspace, stack : Array(Ast::Node))
node.styles.each do |style|
next unless cursor_intersects?(style)

return definition(style, server, workspace, stack)
end
end
end
end
end
16 changes: 6 additions & 10 deletions src/ls/definition/html_style.cr
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
module Mint
module LS
class Definition < LSP::RequestMessage
def html_style(server : Server, workspace : Workspace, stack : Array(Ast::Node))
with_stack(stack) do |reader|
return unless variable = reader.find_next Ast::Variable
def definition(node : Ast::HtmlStyle, server : Server, workspace : Workspace, stack : Array(Ast::Node))
return unless cursor_intersects?(node.name)

return unless reader.find_next Ast::HtmlStyle
return unless component = stack.find(&.is_a?(Ast::Component)).as?(Ast::Component)

return unless component = reader.find_anywhere Ast::Component
return unless component_style =
component.styles.find(&.name.value.== node.name.value)

return unless component_style =
component.styles.find(&.name.value.== variable.value)

location_link server, variable, component_style
end
location_link server, node.name, component_style.name, component_style
end
end
end
Expand Down
22 changes: 0 additions & 22 deletions src/ls/stack_reader.cr

This file was deleted.

2 changes: 1 addition & 1 deletion src/parsers/connect_variable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Mint

def connect_variable
start do |start_position|
value = variable || constant_variable
value = variable(track: false) || constant_variable

next unless value

Expand Down
2 changes: 1 addition & 1 deletion src/parsers/html_attribute.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Mint

def html_attribute(with_dashes : Bool = true, fixed_name : String? = nil) : Ast::HtmlAttribute?
start do |start_position|
name = with_dashes ? variable_attribute_name : variable
name = with_dashes ? variable_attribute_name : variable(track: false)

next unless name
next if fixed_name && name.value != fixed_name
Expand Down
1 change: 0 additions & 1 deletion src/parsers/html_component.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ module Mint
end

next unless component
self << component

ref = start do
whitespace
Expand Down
4 changes: 2 additions & 2 deletions src/parsers/html_style.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Mint
start do |start_position|
name = start do
next unless keyword "::"
next unless value = variable_with_dashes track: true
next unless value = variable_with_dashes track: false
value
end

Expand All @@ -23,7 +23,7 @@ module Mint
char ')', HtmlStyleExpectedClosingParentheses
end

self << Ast::HtmlStyle.new(
Ast::HtmlStyle.new(
arguments: arguments,
from: start_position,
to: position,
Expand Down

0 comments on commit d699a6b

Please sign in to comment.