From b8576d214e436e5238e5064b36d6bc244090fc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szikszai=20Guszt=C3=A1v?= Date: Tue, 18 Jul 2023 17:05:58 +0200 Subject: [PATCH 1/5] Fixes for Crystal 1.9.1 --- .tool-versions | 2 +- shard.lock | 4 ++-- shard.yml | 2 +- src/lsp/protocol/request_message.cr | 3 +++ src/type_checkers/case.cr | 2 +- src/utils/terminal_snippet.cr | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.tool-versions b/.tool-versions index f2b9f0e40..47937bdba 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -crystal 1.8.2 +crystal 1.9.1 mint 0.18.0 diff --git a/shard.lock b/shard.lock index 2771dcba1..7e0566914 100644 --- a/shard.lock +++ b/shard.lock @@ -6,7 +6,7 @@ shards: ameba: git: https://github.com/crystal-ameba/ameba.git - version: 1.4.3 + version: 1.4.3+git.commit.8c9d234d0b06d945d0cf841dfa0596734acc715c backtracer: git: https://github.com/sija/backtracer.cr.git @@ -22,7 +22,7 @@ shards: exception_page: git: https://github.com/crystal-loot/exception_page.git - version: 0.3.0 + version: 0.3.1 kemal: git: https://github.com/kemalcr/kemal.git diff --git a/shard.yml b/shard.yml index 5a959de6b..85f60db55 100644 --- a/shard.yml +++ b/shard.yml @@ -24,7 +24,7 @@ dependencies: development_dependencies: ameba: github: crystal-ameba/ameba - version: ~> 1.4.3 + branch: master targets: mint: diff --git a/src/lsp/protocol/request_message.cr b/src/lsp/protocol/request_message.cr index ec359c1ad..fd3b7b6fb 100644 --- a/src/lsp/protocol/request_message.cr +++ b/src/lsp/protocol/request_message.cr @@ -5,6 +5,9 @@ module LSP abstract class RequestMessage include JSON::Serializable + # WHY IS THIS NEEDED FOR 1.9.1 and not for 1.8?????? + property snippet_support : Bool? + # The request id. property id : Int32 | String diff --git a/src/type_checkers/case.cr b/src/type_checkers/case.cr index 2eb458276..e137aff05 100644 --- a/src/type_checkers/case.cr +++ b/src/type_checkers/case.cr @@ -102,7 +102,7 @@ module Mint if destructurings.empty? true else - (1..destructurings.map(&.items.size).max).to_a.all? do |length| + (1..destructurings.max_of(&.items.size)).to_a.all? do |length| destructurings.any?(&.covers?(length)) end end diff --git a/src/utils/terminal_snippet.cr b/src/utils/terminal_snippet.cr index fd7e9d6fb..9e5dd2e68 100644 --- a/src/utils/terminal_snippet.cr +++ b/src/utils/terminal_snippet.cr @@ -76,7 +76,7 @@ module Mint lines[start_line, end_line - start_line] min_width = { - relevant_lines.map(&.size).max + gutter_width + 5, + relevant_lines.max_of(&.size) + gutter_width + 5, width, }.max From ab003974d2038cf388c129ce829cf65911cf697a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szikszai=20Guszt=C3=A1v?= Date: Tue, 18 Jul 2023 17:17:07 +0200 Subject: [PATCH 2/5] Update Dockerfile as well. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f98b59612..c12a06a05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # We are using crystal as image we are building the binary on -FROM crystallang/crystal:1.8.2-alpine AS build +FROM crystallang/crystal:1.9.1-alpine AS build # Create a build directory and set it as default RUN mkdir -p /opt/mint From 1a9f3f483ad4ca864a9f32886a061e5a2fca7bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szikszai=20Guszt=C3=A1v?= Date: Wed, 19 Jul 2023 11:55:41 +0200 Subject: [PATCH 3/5] Semantic Tokenizer (#615) Co-authored-by: jansul Co-authored-by: Sijawusz Pur Rahnama --- spec/language_server/ls_spec.cr | 2 +- spec/language_server/semantic_tokens/record | 87 +++++++++ src/all.cr | 2 + src/ast.cr | 7 +- src/ast/directives/highlight.cr | 15 ++ src/ast/html_component.cr | 5 +- src/ast/html_element.cr | 2 + src/ast/node.cr | 12 +- src/cli.cr | 1 + src/commands/highlight.cr | 20 ++ src/compilers/directives/highlight.cr | 36 ++++ src/formatters/directives/highlight.cr | 7 + src/ls/definition/type_id.cr | 24 +++ src/ls/initialize.cr | 10 + src/ls/semantic_tokens.cr | 65 +++++++ src/ls/server.cr | 3 +- .../semantic_tokens_client_capabilities.cr | 16 ++ src/lsp/protocol/semantic_tokens_legend.cr | 16 ++ src/lsp/protocol/semantic_tokens_options.cr | 18 ++ src/lsp/protocol/semantic_tokens_params.cr | 9 + src/lsp/protocol/server_capabilities.cr | 5 + .../text_document_client_capabilities.cr | 4 + src/parser.cr | 7 + src/parsers/basic_expression.cr | 1 + src/parsers/code_block.cr | 13 ++ src/parsers/comment.cr | 2 +- src/parsers/directives/highlight.cr | 25 +++ src/parsers/html_body.cr | 6 +- src/parsers/html_component.cr | 12 +- src/parsers/html_element.cr | 12 +- src/parsers/operation.cr | 2 + src/parsers/type_id.cr | 20 +- src/semantic_tokenizer.cr | 173 ++++++++++++++++++ src/type_checkers/directives/highlight.cr | 7 + 34 files changed, 616 insertions(+), 30 deletions(-) create mode 100644 spec/language_server/semantic_tokens/record create mode 100644 src/ast/directives/highlight.cr create mode 100644 src/commands/highlight.cr create mode 100644 src/compilers/directives/highlight.cr create mode 100644 src/formatters/directives/highlight.cr create mode 100644 src/ls/definition/type_id.cr create mode 100644 src/ls/semantic_tokens.cr create mode 100644 src/lsp/protocol/semantic_tokens_client_capabilities.cr create mode 100644 src/lsp/protocol/semantic_tokens_legend.cr create mode 100644 src/lsp/protocol/semantic_tokens_options.cr create mode 100644 src/lsp/protocol/semantic_tokens_params.cr create mode 100644 src/parsers/directives/highlight.cr create mode 100644 src/semantic_tokenizer.cr create mode 100644 src/type_checkers/directives/highlight.cr diff --git a/spec/language_server/ls_spec.cr b/spec/language_server/ls_spec.cr index 78fd357a1..ef9b05518 100644 --- a/spec/language_server/ls_spec.cr +++ b/spec/language_server/ls_spec.cr @@ -5,7 +5,7 @@ def clean_json(workspace : Workspace, path : String) end Dir - .glob("./spec/language_server/{definition,hover}/**/*") + .glob("./spec/language_server/{definition,hover,semantic_tokens}/**/*") .select! { |file| File.file?(file) } .sort! .each do |file| diff --git a/spec/language_server/semantic_tokens/record b/spec/language_server/semantic_tokens/record new file mode 100644 index 000000000..deadce430 --- /dev/null +++ b/spec/language_server/semantic_tokens/record @@ -0,0 +1,87 @@ +/* Some comment. */ +record Article { + id : Number, + description : String, + title : String +} +-----------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "semanticTokens": { + "dynamicRegistration": false, + "tokenTypes": ["property"] + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + } + }, + "method": "textDocument/semanticTokens/full" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "data": [ + 0, + 0, + 20, + 5, + 0, + 1, + 0, + 6, + 4, + 0, + 0, + 7, + 7, + 1, + 0, + 1, + 2, + 2, + 6, + 0, + 0, + 5, + 6, + 1, + 0, + 1, + 2, + 11, + 6, + 0, + 0, + 14, + 6, + 1, + 0, + 1, + 2, + 5, + 6, + 0, + 0, + 8, + 6, + 1, + 0 + ] + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/src/all.cr b/src/all.cr index de99cefb9..c9f6e48d1 100644 --- a/src/all.cr +++ b/src/all.cr @@ -57,6 +57,8 @@ require "./documentation_generator/**" require "./documentation_generator" require "./documentation_server" +require "./semantic_tokenizer" + require "./test_runner/**" require "./test_runner" diff --git a/src/ast.cr b/src/ast.cr index 9e123e314..539e32c83 100644 --- a/src/ast.cr +++ b/src/ast.cr @@ -31,9 +31,12 @@ module Mint Js getter components, modules, records, stores, routes, providers - getter suites, enums, comments, nodes, unified_modules + getter suites, enums, comments, nodes, unified_modules, keywords + getter operators - def initialize(@records = [] of RecordDefinition, + def initialize(@operators = [] of Tuple(Int32, Int32), + @keywords = [] of Tuple(Int32, Int32), + @records = [] of RecordDefinition, @unified_modules = [] of Module, @components = [] of Component, @providers = [] of Provider, diff --git a/src/ast/directives/highlight.cr b/src/ast/directives/highlight.cr new file mode 100644 index 000000000..3f4ac2ba0 --- /dev/null +++ b/src/ast/directives/highlight.cr @@ -0,0 +1,15 @@ +module Mint + class Ast + module Directives + class Highlight < Node + getter content + + def initialize(@content : Block, + @input : Data, + @from : Int32, + @to : Int32) + end + end + end + end +end diff --git a/src/ast/html_component.cr b/src/ast/html_component.cr index 99fefbdff..f999db33b 100644 --- a/src/ast/html_component.cr +++ b/src/ast/html_component.cr @@ -1,13 +1,14 @@ module Mint class Ast class HtmlComponent < Node - getter attributes, children, component, comments, ref + getter attributes, children, component, comments, ref, closing_tag_position def initialize(@attributes : Array(HtmlAttribute), + @closing_tag_position : Int32?, @comments : Array(Comment), @children : Array(Node), - @ref : Variable?, @component : TypeId, + @ref : Variable?, @input : Data, @from : Int32, @to : Int32) diff --git a/src/ast/html_element.cr b/src/ast/html_element.cr index de4136589..d587353c6 100644 --- a/src/ast/html_element.cr +++ b/src/ast/html_element.cr @@ -2,8 +2,10 @@ module Mint class Ast class HtmlElement < Node getter attributes, children, styles, tag, comments, ref + getter closing_tag_position def initialize(@attributes : Array(HtmlAttribute), + @closing_tag_position : Int32?, @comments : Array(Comment), @styles : Array(HtmlStyle), @children : Array(Node), diff --git a/src/ast/node.cr b/src/ast/node.cr index 725b64d8f..9a1565fa6 100644 --- a/src/ast/node.cr +++ b/src/ast/node.cr @@ -71,7 +71,7 @@ module Mint source.strip.includes?('\n') end - protected def compute_position(lines, needle) : Position + def self.compute_position(lines, needle) : Position line_start_pos, line = begin left, right = 0, lines.size - 1 index = pos = 0 @@ -107,19 +107,23 @@ module Mint {line, column} end - getter location : Location do + def self.compute_location(input : Data, from, to) # TODO: avoid creating this array for every (initial) call to `Node#location` lines = [0] - @input.input.each_char_with_index do |ch, i| + input.input.each_char_with_index do |ch, i| lines << i + 1 if ch == '\n' end Location.new( - filename: @input.file, + filename: input.file, start: compute_position(lines, from), end: compute_position(lines, to), ) end + + getter location : Location do + Node.compute_location(input, from, to) + end end end end diff --git a/src/cli.cr b/src/cli.cr index 050e9f48f..dad26230e 100644 --- a/src/cli.cr +++ b/src/cli.cr @@ -15,6 +15,7 @@ module Mint define_help description: "Mint" register_sub_command "sandbox-server", type: SandboxServer + register_sub_command highlight, type: Highlight register_sub_command install, type: Install register_sub_command compile, type: Compile register_sub_command version, type: Version diff --git a/src/commands/highlight.cr b/src/commands/highlight.cr new file mode 100644 index 000000000..6b1195f0a --- /dev/null +++ b/src/commands/highlight.cr @@ -0,0 +1,20 @@ +module Mint + class Cli < Admiral::Command + class Highlight < Admiral::Command + include Command + + define_help description: "Returns the syntax highlighted version of the given file" + + define_argument path, description: "The path to the file" + + define_flag html : Bool, + description: "If specified, print the highlighted code as HTML", + default: false + + def run + return unless path = arguments.path + puts SemanticTokenizer.highlight(path, flags.html) + end + end + end +end diff --git a/src/compilers/directives/highlight.cr b/src/compilers/directives/highlight.cr new file mode 100644 index 000000000..8690696a9 --- /dev/null +++ b/src/compilers/directives/highlight.cr @@ -0,0 +1,36 @@ +module Mint + class Compiler + def _compile(node : Ast::Directives::Highlight) : String + content = + compile node.content + + formatted = + Formatter.new.format(node.content, Formatter::BlockFormat::Naked) + + parser = Parser.new(formatted, "source.mint") + parser.code_block_naked + + parts = + SemanticTokenizer.tokenize(parser.ast) + + mapped = + parts.map do |item| + case item + in String + "`#{skip { escape_for_javascript(item) }}`" + in Tuple(SemanticTokenizer::TokenType, String) + "_h('span', { className: '#{item[0].to_s.underscore}' }, [`#{skip { escape_for_javascript(item[1]) }}`])" + end + end + + "[#{content}, _h(React.Fragment, {}, [#{mapped.join(",\n")}])]" + end + + def escape_for_javascript(value : String) + value + .gsub('\\', "\\\\") + .gsub('`', "\\`") + .gsub("${", "\\${") + end + end +end diff --git a/src/formatters/directives/highlight.cr b/src/formatters/directives/highlight.cr new file mode 100644 index 000000000..ba0408468 --- /dev/null +++ b/src/formatters/directives/highlight.cr @@ -0,0 +1,7 @@ +module Mint + class Formatter + def format(node : Ast::Directives::Highlight) + "@highlight #{format(node.content)}" + end + end +end diff --git a/src/ls/definition/type_id.cr b/src/ls/definition/type_id.cr new file mode 100644 index 000000000..eed5a2a24 --- /dev/null +++ b/src/ls/definition/type_id.cr @@ -0,0 +1,24 @@ +module Mint + module LS + class Definition < LSP::RequestMessage + def definition(node : Ast::TypeId, server : Server, workspace : Workspace, stack : Array(Ast::Node)) + found = + workspace.ast.enums.find(&.name.value.==(node.value)) || + workspace.ast.records.find(&.name.value.==(node.value)) || + workspace.ast.stores.find(&.name.value.==(node.value)) || + find_component(workspace, node.value) + + if found.nil? && (next_node = stack[1]) + return definition(next_node, server, workspace, stack) + end + + return if Core.ast.nodes.includes?(found) + + case found + when Ast::Store, Ast::Enum, Ast::Component, Ast::RecordDefinition + location_link server, node, found.name, found + end + end + end + end +end diff --git a/src/ls/initialize.cr b/src/ls/initialize.cr index 0d7305fff..4a712d096 100644 --- a/src/ls/initialize.cr +++ b/src/ls/initialize.cr @@ -42,10 +42,20 @@ module Mint change_notifications: false, supported: false)) + semantic_tokens_provider = + LSP::SemanticTokensOptions.new( + range: false, + full: true, + legend: LSP::SemanticTokensLegend.new( + token_types: SemanticTokenizer::TOKEN_TYPES, + token_modifiers: [] of String, + )) + capabilities = LSP::ServerCapabilities.new( document_on_type_formatting_provider: document_on_type_formatting_provider, execute_command_provider: execute_command_provider, + semantic_tokens_provider: semantic_tokens_provider, signature_help_provider: signature_help_provider, document_link_provider: document_link_provider, completion_provider: completion_provider, diff --git a/src/ls/semantic_tokens.cr b/src/ls/semantic_tokens.cr new file mode 100644 index 000000000..aeb24a56d --- /dev/null +++ b/src/ls/semantic_tokens.cr @@ -0,0 +1,65 @@ +module Mint + module LS + # This is the class that handles the "textDocument/semanticTokens/full" request. + class SemanticTokens < LSP::RequestMessage + property params : LSP::SemanticTokensParams + + def execute(server) + uri = + URI.parse(params.text_document.uri) + + ast = + Workspace[uri.path.to_s][uri.path.to_s] + + # This is used later on to convert the line/column of each token + input = + ast.nodes.first.input + + tokenizer = SemanticTokenizer.new + tokenizer.tokenize(ast) + + data = + tokenizer.tokens.sort_by(&.from).compact_map do |token| + location = + Ast::Node.compute_location(input, token.from, token.to) + + type = + token.type.to_s.underscore + + if index = SemanticTokenizer::TOKEN_TYPES.index(type) + [ + location.start[0] - 1, + location.start[1], + token.to - token.from, + index, + 0, + ] + end + end + + result = [] of Array(Int32) + + data.each_with_index do |item, index| + current = + item.dup + + unless index.zero? + last = + data[index - 1] + + current[0] = + current[0] - last[0] + + current[1] = current[1] - last[1] if current[0] == 0 + end + + result << current + end + + { + data: result.flatten, + } + end + end + end +end diff --git a/src/ls/server.cr b/src/ls/server.cr index 98d3ba3c1..9166feae6 100644 --- a/src/ls/server.cr +++ b/src/ls/server.cr @@ -8,13 +8,14 @@ module Mint # Text document related methods method "textDocument/willSaveWaitUntil", WillSaveWaitUntil + method "textDocument/semanticTokens/full", SemanticTokens method "textDocument/foldingRange", FoldingRange method "textDocument/formatting", Formatting method "textDocument/completion", Completion method "textDocument/codeAction", CodeAction + method "textDocument/definition", Definition method "textDocument/didChange", DidChange method "textDocument/hover", Hover - method "textDocument/definition", Definition property params : LSP::InitializeParams? = nil diff --git a/src/lsp/protocol/semantic_tokens_client_capabilities.cr b/src/lsp/protocol/semantic_tokens_client_capabilities.cr new file mode 100644 index 000000000..09349a289 --- /dev/null +++ b/src/lsp/protocol/semantic_tokens_client_capabilities.cr @@ -0,0 +1,16 @@ +module LSP + struct SemanticTokensClientCapabilities + include JSON::Serializable + + # Whether definition supports dynamic registration. + @[JSON::Field(key: "dynamicRegistration")] + property dynamic_registration : Bool? + + # The token types that the client supports. + @[JSON::Field(key: "tokenTypes")] + property token_types : Array(String)? + + def initialize(@dynamic_registration = nil, @token_types = nil) + end + end +end diff --git a/src/lsp/protocol/semantic_tokens_legend.cr b/src/lsp/protocol/semantic_tokens_legend.cr new file mode 100644 index 000000000..55de94a39 --- /dev/null +++ b/src/lsp/protocol/semantic_tokens_legend.cr @@ -0,0 +1,16 @@ +module LSP + struct SemanticTokensLegend + include JSON::Serializable + + # The token types a server uses. + @[JSON::Field(key: "tokenTypes")] + property token_types : Array(String) + + # The token modifiers a server uses. + @[JSON::Field(key: "tokenModifiers")] + property token_modifiers : Array(String) + + def initialize(@token_types, @token_modifiers) + end + end +end diff --git a/src/lsp/protocol/semantic_tokens_options.cr b/src/lsp/protocol/semantic_tokens_options.cr new file mode 100644 index 000000000..49b2dd1b0 --- /dev/null +++ b/src/lsp/protocol/semantic_tokens_options.cr @@ -0,0 +1,18 @@ +module LSP + struct SemanticTokensOptions + include JSON::Serializable + + # The legend used by the server + property legend : SemanticTokensLegend + + # Server supports providing semantic tokens for a specific range + # of a document. + property? range : Bool + + # Server supports providing semantic tokens for a full document. + property? full : Bool + + def initialize(@legend, @range, @full) + end + end +end diff --git a/src/lsp/protocol/semantic_tokens_params.cr b/src/lsp/protocol/semantic_tokens_params.cr new file mode 100644 index 000000000..175b89d29 --- /dev/null +++ b/src/lsp/protocol/semantic_tokens_params.cr @@ -0,0 +1,9 @@ +module LSP + class SemanticTokensParams + include JSON::Serializable + + # The document in which the command was invoked. + @[JSON::Field(key: "textDocument")] + property text_document : TextDocumentIdentifier + end +end diff --git a/src/lsp/protocol/server_capabilities.cr b/src/lsp/protocol/server_capabilities.cr index b67cbef19..d3d1e18ca 100644 --- a/src/lsp/protocol/server_capabilities.cr +++ b/src/lsp/protocol/server_capabilities.cr @@ -96,6 +96,10 @@ module LSP @[JSON::Field(key: "executeCommandProvider")] property execute_command_provider : ExecuteCommandOptions + # The server provides semantic tokens support. + @[JSON::Field(key: "semanticTokensProvider")] + property semantic_tokens_provider : SemanticTokensOptions + # Workspace specific server capabilities property workspace : Workspace @@ -105,6 +109,7 @@ module LSP @document_formatting_provider, @document_highlight_provider, @workspace_symbol_provider, + @semantic_tokens_provider, @document_symbol_provider, @type_definition_provider, @execute_command_provider, diff --git a/src/lsp/protocol/text_document_client_capabilities.cr b/src/lsp/protocol/text_document_client_capabilities.cr index b2a8af71e..8aab5e82f 100644 --- a/src/lsp/protocol/text_document_client_capabilities.cr +++ b/src/lsp/protocol/text_document_client_capabilities.cr @@ -7,5 +7,9 @@ module LSP # Capabilities specific to the `textDocument/definition` request. property definition : DefinitionClientCapabilities? + + # Capabilities specific to the `textDocument/semanticTokens` request. + @[JSON::Field(key: "semanticTokens")] + property semantic_tokens : SemanticTokensClientCapabilities? end end diff --git a/src/parser.cr b/src/parser.cr index 0f6da5c6c..c2d9083a8 100644 --- a/src/parser.cr +++ b/src/parser.cr @@ -22,13 +22,16 @@ module Mint def start(&) start_position = position + node_size = ast.nodes.size begin node = yield position @position = start_position unless node + ast.nodes.delete_at(node_size...) unless node node rescue error : Error @position = start_position + ast.nodes.delete_at(node_size...) raise error end end @@ -180,6 +183,10 @@ module Mint def keyword(word) : Bool if keyword_ahead?(word) + if word.chars.all?(&.ascii_lowercase?) && !word.blank? && word != "or" + @ast.keywords << {position, position + word.size} + end + @position += word.size true else diff --git a/src/parsers/basic_expression.cr b/src/parsers/basic_expression.cr index 49a3e701b..051489ac5 100644 --- a/src/parsers/basic_expression.cr +++ b/src/parsers/basic_expression.cr @@ -3,6 +3,7 @@ module Mint # NOTE: The order of the parsing is important! def basic_expression : Ast::Expression? format_directive || + highlight_directive || documentation_directive || svg_directive || asset_directive || diff --git a/src/parsers/code_block.cr b/src/parsers/code_block.cr index 6c3717e27..1b943e522 100644 --- a/src/parsers/code_block.cr +++ b/src/parsers/code_block.cr @@ -1,5 +1,18 @@ module Mint class Parser + def code_block_naked : Ast::Block? + start do |start_position| + statements = + many { comment || statement } + + self << Ast::Block.new( + statements: statements, + from: start_position, + to: position, + input: data) if statements + end + end + def code_block : Ast::Block? start do |start_position| statements = diff --git a/src/parsers/comment.cr b/src/parsers/comment.cr index 44b4af94c..2ddad63b0 100644 --- a/src/parsers/comment.cr +++ b/src/parsers/comment.cr @@ -24,7 +24,7 @@ module Mint whitespace - Ast::Comment.new( + self << Ast::Comment.new( from: start_position, value: value, type: type, diff --git a/src/parsers/directives/highlight.cr b/src/parsers/directives/highlight.cr new file mode 100644 index 000000000..e273354a7 --- /dev/null +++ b/src/parsers/directives/highlight.cr @@ -0,0 +1,25 @@ +module Mint + class Parser + syntax_error FormatDirectiveExpectedOpeningBracket + syntax_error FormatDirectiveExpectedClosingBracket + syntax_error FormatDirectiveExpectedExpression + + def highlight_directive : Ast::Directives::Highlight? + start do |start_position| + next unless keyword "@highlight" + + content = + code_block( + opening_bracket: FormatDirectiveExpectedOpeningBracket, + closing_bracket: FormatDirectiveExpectedClosingBracket, + statement_error: FormatDirectiveExpectedExpression) + + self << Ast::Directives::Highlight.new( + from: start_position, + content: content, + to: position, + input: data) + end + end + end +end diff --git a/src/parsers/html_body.cr b/src/parsers/html_body.cr index b9a99d3d6..85183c3dd 100644 --- a/src/parsers/html_body.cr +++ b/src/parsers/html_body.cr @@ -44,6 +44,9 @@ module Mint tag end + closing_tag_position = + position + 2 + raise expected_closing_tag, position, { "opening_tag" => tag, } unless keyword "" @@ -60,7 +63,8 @@ module Mint {attributes, children, - comments} + comments, + closing_tag_position} end end end diff --git a/src/parsers/html_component.cr b/src/parsers/html_component.cr index 88d32eeb7..4147ac54e 100644 --- a/src/parsers/html_component.cr +++ b/src/parsers/html_component.cr @@ -21,13 +21,15 @@ module Mint variable! HtmlComponentExpectedReference end - attributes, children, comments = html_body( - expected_closing_bracket: HtmlComponentExpectedClosingBracket, - expected_closing_tag: HtmlComponentExpectedClosingTag, - with_dashes: false, - tag: component) + attributes, children, comments, closing_tag_position = + html_body( + expected_closing_bracket: HtmlComponentExpectedClosingBracket, + expected_closing_tag: HtmlComponentExpectedClosingTag, + with_dashes: false, + tag: component) node = self << Ast::HtmlComponent.new( + closing_tag_position: closing_tag_position, attributes: attributes, from: start_position, component: component, diff --git a/src/parsers/html_element.cr b/src/parsers/html_element.cr index b9a888091..5c2db7e21 100644 --- a/src/parsers/html_element.cr +++ b/src/parsers/html_element.cr @@ -30,13 +30,15 @@ module Mint variable! HtmlElementExpectedReference end - attributes, children, comments = html_body( - expected_closing_bracket: HtmlElementExpectedClosingBracket, - expected_closing_tag: HtmlElementExpectedClosingTag, - with_dashes: true, - tag: tag) + attributes, children, comments, closing_tag_position = + html_body( + expected_closing_bracket: HtmlElementExpectedClosingBracket, + expected_closing_tag: HtmlElementExpectedClosingTag, + with_dashes: true, + tag: tag) node = self << Ast::HtmlElement.new( + closing_tag_position: closing_tag_position, attributes: attributes, from: start_position, children: children, diff --git a/src/parsers/operation.cr b/src/parsers/operation.cr index 1ee471b8b..249db7cd0 100644 --- a/src/parsers/operation.cr +++ b/src/parsers/operation.cr @@ -30,9 +30,11 @@ module Mint def operator : String? start do whitespace + saved_position = position operator = OPERATORS.keys.find { |item| keyword item } next unless operator next unless whitespace? + ast.operators << {saved_position, saved_position + operator.size} whitespace operator end diff --git a/src/parsers/type_id.cr b/src/parsers/type_id.cr index 56f55916a..d8734533c 100644 --- a/src/parsers/type_id.cr +++ b/src/parsers/type_id.cr @@ -1,6 +1,6 @@ module Mint class Parser - def type_id!(error : SyntaxError.class) : Ast::TypeId + def type_id!(error : SyntaxError.class, *, track : Bool = true) : Ast::TypeId start do |start_position| value = gather do char(error, &.ascii_uppercase?) @@ -10,7 +10,7 @@ module Mint raise error unless value if char! '.' - other = type_id! error + other = type_id!(error, track: false) value += ".#{other.value}" end @@ -18,11 +18,13 @@ module Mint from: start_position, value: value, to: position, - input: data) + input: data).tap do |node| + self << node if track + end end end - def type_id : Ast::TypeId? + def type_id(*, track : Bool = true) : Ast::TypeId? start do |start_position| value = gather do return unless char.ascii_uppercase? @@ -36,7 +38,7 @@ module Mint if char == '.' other = start do step - next_part = type_id + next_part = type_id(track: false) next unless next_part next_part end @@ -51,13 +53,15 @@ module Mint from: start_position, value: value, to: position, - input: data) + input: data).tap do |node| + self << node if track + end end end - def type_id(error : SyntaxError.class) : Ast::TypeId? + def type_id(error : SyntaxError.class, *, track : Bool = true) : Ast::TypeId? return unless char.ascii_uppercase? - type_id! error + type_id! error, track: track end end end diff --git a/src/semantic_tokenizer.cr b/src/semantic_tokenizer.cr new file mode 100644 index 000000000..c359bfcff --- /dev/null +++ b/src/semantic_tokenizer.cr @@ -0,0 +1,173 @@ +module Mint + class SemanticTokenizer + # This is a subset of the LSPs SemanticTokenTypes enum. + enum TokenType + TypeParameter + Type + + Namespace + Property + Keyword + Comment + + Variable + Operator + String + Number + Regexp + end + + TOKEN_TYPES = TokenType.names.map!(&.camelcase(lower: true)) + + # This represents which token types are used for which node. + TOKEN_MAP = { + Ast::TypeVariable => TokenType::TypeParameter, + Ast::Variable => TokenType::Variable, + Ast::Comment => TokenType::Comment, + Ast::StringLiteral => TokenType::String, + Ast::RegexpLiteral => TokenType::Regexp, + Ast::NumberLiteral => TokenType::Number, + Ast::TypeId => TokenType::Type, + } + + # Represents a semantic token using the positions of the token instead + # of line / column (for the LSP it is converted to line /column). + record Token, + type : TokenType, + from : Int32, + to : Int32 + + # We keep a cache of all tokenized nodes to avoid duplications + getter cache = Set(Ast::Node).new + + # This is where the resulting tokens are stored. + getter tokens = [] of Token + + def self.tokenize(ast : Ast) + tokenizer = self.new + tokenizer.tokenize(ast) + + parts = [] of String | Tuple(SemanticTokenizer::TokenType, String) + contents = ast.nodes.first.input.input + position = 0 + + tokenizer.tokens.sort_by(&.from).each do |token| + if token.from > position + parts << contents[position, token.from - position] + end + + parts << {token.type, contents[token.from, token.to - token.from]} + position = token.to + end + + if position < contents.size + parts << contents[position, contents.size] + end + + parts + end + + def self.highlight(path : String, html : Bool = false) + ast = + Parser.parse(path) + + parts = + tokenize(ast) + + parts.join do |item| + case item + in String + html ? HTML.escape(item) : item + in Tuple(SemanticTokenizer::TokenType, String) + if html + next "#{HTML.escape(item[1])}" + end + + case item[0] + in .type? + item[1].colorize(:yellow) + in .type_parameter? + item[1].colorize(:light_yellow) + in .variable? + item[1].colorize(:dark_gray) + in .namespace? + item[1].colorize(:light_blue) + in .keyword? + item[1].colorize(:magenta) + in .property? + item[1].colorize(:dark_gray).mode(:underline) + in .comment? + item[1].colorize(:light_gray) + in .string? + item[1].colorize(:green) + in .number? + item[1].colorize(:red) + in .regexp? + item[1].colorize(:light_red) + in .operator? + item[1].colorize(:light_magenta) + end.to_s + end + end + end + + def tokenize(ast : Ast) + # We add the operators and keywords directly from the AST + ast.operators.each { |(from, to)| add(from, to, :operator) } + ast.keywords.each { |(from, to)| add(from, to, :keyword) } + + tokenize(ast.nodes) + end + + def tokenize(nodes : Array(Ast::Node)) + nodes.each { |node| tokenize(node) } + end + + def tokenize(node : Ast::Node?) + if type = TOKEN_MAP[node.class]? + add(node, type) + end + end + + def tokenize(node : Ast::CssDefinition) + add(node.from, node.from + node.name.size, :property) + end + + def tokenize(node : Ast::ArrayAccess) + # TODO: The index should be parsed as a number literal when + # implemented remove this + case index = node.index + when Int64 + add(node.from + 1, node.from + 1 + index.to_s.size, :number) + end + end + + def tokenize(node : Ast::HtmlElement) + # The closing tag is not saved only the position to it. + node.closing_tag_position.try do |position| + add(position, position + node.tag.value.size, :namespace) + end + + add(node.tag, TokenType::Namespace) + end + + def tokenize(node : Ast::HtmlComponent) + node.closing_tag_position.try do |position| + add(position, position + node.component.value.size, :type) + end + end + + def add(from : Int32, to : Int32, type : TokenType) + tokens << Token.new( + type: type, + from: from, + to: to) + end + + def add(node : Ast::Node, type : TokenType) + return if cache.includes?(node) + add(node.from, node.to, type) + cache.add(node) + end + end +end diff --git a/src/type_checkers/directives/highlight.cr b/src/type_checkers/directives/highlight.cr new file mode 100644 index 000000000..9e47bf932 --- /dev/null +++ b/src/type_checkers/directives/highlight.cr @@ -0,0 +1,7 @@ +module Mint + class TypeChecker + def check(node : Ast::Directives::Highlight) : Checkable + Type.new("Tuple", [resolve(node.content), HTML] of Checkable) + end + end +end From 104a2aee79cdcc56e0ed1416d915741ff9991d57 Mon Sep 17 00:00:00 2001 From: jansul Date: Sat, 22 Jul 2023 18:54:36 +0100 Subject: [PATCH 4/5] Fix constants/gets not working within a Provider (#621) --- spec/compilers/provider_with_items | 102 ++++++++++++++++++++++++++++ spec/formatters/constant_provider | 24 +++++++ spec/formatters/provider_with_items | 34 ++++++++++ spec/type_checking/provider_const | 24 +++++++ src/formatters/provider.cr | 2 +- src/parsers/provider.cr | 2 +- src/type_checker/scope.cr | 2 +- 7 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 spec/compilers/provider_with_items create mode 100644 spec/formatters/constant_provider create mode 100644 spec/formatters/provider_with_items create mode 100644 spec/type_checking/provider_const diff --git a/spec/compilers/provider_with_items b/spec/compilers/provider_with_items new file mode 100644 index 000000000..e1cfe9c0e --- /dev/null +++ b/spec/compilers/provider_with_items @@ -0,0 +1,102 @@ +record Subscription { + a : Bool, + b : Bool +} + +provider Provider : Subscription { + const NAME = "hello" + + state a : String = "" + + get b : String { + a + } + + fun name : String { + NAME + } +} + +component Main { + use Provider { + a: true, + b: false + } + + fun render { +
+ } +} +-------------------------------------------------------------------------------- +const A = _R({ + a: [ + "a", + Decoder.boolean + ], + b: [ + "b", + Decoder.boolean + ] +}); + +const B = new(class extends _P { + constructor() { + super(); + + this.state = { + c: `` + }; + + this._d({ + b: () => { + return `hello` + } + }); + } + + get c() { + return this.state.c; + } + + get d() { + return this.c + } + + a() { + return this.b; + } +}); + +class C extends _C { + componentWillUnmount() { + B._unsubscribe(this); + } + + componentDidUpdate() { + if (true) { + B._subscribe(this, new A({ + a: true, + b: false + })) + } else { + B._unsubscribe(this) + }; + } + + componentDidMount() { + if (true) { + B._subscribe(this, new A({ + a: true, + b: false + })) + } else { + B._unsubscribe(this) + }; + } + + render() { + return _h("div", {}); + } +}; + +C.displayName = "Main"; diff --git a/spec/formatters/constant_provider b/spec/formatters/constant_provider new file mode 100644 index 000000000..4e2eb6646 --- /dev/null +++ b/spec/formatters/constant_provider @@ -0,0 +1,24 @@ +record Subscription { + a : Bool +} + +provider Provider:Subscription { + /* Comment */constNAME="hello" + + fun name:String { + NAME + } +} +-------------------------------------------------------------------------------- +record Subscription { + a : Bool +} + +provider Provider : Subscription { + /* Comment */ + const NAME = "hello" + + fun name : String { + NAME + } +} diff --git a/spec/formatters/provider_with_items b/spec/formatters/provider_with_items new file mode 100644 index 000000000..4efc415f6 --- /dev/null +++ b/spec/formatters/provider_with_items @@ -0,0 +1,34 @@ +record Subscription { + a : Bool +} + +provider Provider:Subscription { +/* Comment */constNAME="hello" + + state a :String = "" + getb:String { + a + } + fun name:String { + NAME + } +} +-------------------------------------------------------------------------------- +record Subscription { + a : Bool +} + +provider Provider : Subscription { + /* Comment */ + const NAME = "hello" + + state a : String = "" + + get b : String { + a + } + + fun name : String { + NAME + } +} diff --git a/spec/type_checking/provider_const b/spec/type_checking/provider_const new file mode 100644 index 000000000..8ea67f70b --- /dev/null +++ b/spec/type_checking/provider_const @@ -0,0 +1,24 @@ +record Subscription { + a : Bool +} + +provider Provider : Subscription { + const ONE_PLUS_ONE = 1 + 1 + const TWO = 2 + + fun test : Bool { + ONE_PLUS_ONE == TWO + } +} +-----------------------------------------------------------------VariableMissing +record Subscription { + a : Bool +} + +provider Provider : Subscription { + const TWO = 2 + + fun test : Bool { + 1 + TWO == THREE + } +} diff --git a/src/formatters/provider.cr b/src/formatters/provider.cr index 531f472ad..8d95fd3e1 100644 --- a/src/formatters/provider.cr +++ b/src/formatters/provider.cr @@ -8,7 +8,7 @@ module Mint node.subscription body = - list node.functions + node.comments + node.states + node.gets + list node.functions + node.comments + node.states + node.gets + node.constants comment = node.comment.try { |item| "#{format(item)}\n" } diff --git a/src/parsers/provider.cr b/src/parsers/provider.cr index 74709263e..b247bf75e 100644 --- a/src/parsers/provider.cr +++ b/src/parsers/provider.cr @@ -26,7 +26,7 @@ module Mint opening_bracket: ProviderExpectedOpeningBracket, closing_bracket: ProviderExpectedClosingBracket ) do - items = many { function || state || constant || self.comment } + items = many { function || state || get || constant || self.comment } raise ProviderExpectedBody if items.none?(Ast::Function) diff --git a/src/type_checker/scope.cr b/src/type_checker/scope.cr index 6b5340a74..3ca15c7af 100644 --- a/src/type_checker/scope.cr +++ b/src/type_checker/scope.cr @@ -156,7 +156,7 @@ module Mint node.functions.find(&.name.value.==(variable)) || node.states.find(&.name.value.==(variable)) || node.gets.find(&.name.value.==(variable)) || - node.constants.find(&.name.==(variable)) + node.constants.find(&.name.value.==(variable)) end end From f00a5c6327bd52f00460c43d766b7843114e7893 Mon Sep 17 00:00:00 2001 From: jansul Date: Sun, 23 Jul 2023 05:11:09 +0100 Subject: [PATCH 5/5] LSP: Add Go to Definition support for `Ast::Variable` (#619) --- .../location/variable_block_statement_target | 55 ++++++++++ .../variable_casebranch_enumdestructuring | 61 +++++++++++ .../location/variable_component_connect | 60 +++++++++++ .../location/variable_component_connect_as | 60 +++++++++++ .../location/variable_component_constant | 57 ++++++++++ .../location/variable_component_function | 59 ++++++++++ .../location/variable_component_get | 59 ++++++++++ .../location/variable_component_property | 57 ++++++++++ .../location/variable_component_state | 57 ++++++++++ .../definition/location/variable_for | 55 ++++++++++ .../location/variable_for_argument_stop | 43 ++++++++ .../definition/location/variable_for_subject | 55 ++++++++++ .../location/variable_function_argument | 53 +++++++++ .../location/variable_function_argument_stop | 29 +++++ .../location/variable_module_constant | 55 ++++++++++ .../location/variable_nextcall_component | 59 ++++++++++ .../location/variable_nextcall_record | 74 +++++++++++++ .../location/variable_nextcall_store | 55 ++++++++++ .../location/variable_nextcall_value | 58 ++++++++++ .../location/variable_provider_constant | 59 ++++++++++ .../location/variable_recordfield_key | 63 +++++++++++ .../location/variable_recordfield_value | 63 +++++++++++ .../location/variable_store_constant | 55 ++++++++++ .../location/variable_store_function | 57 ++++++++++ .../definition/location/variable_store_get | 61 +++++++++++ .../definition/location/variable_store_state | 59 ++++++++++ .../location/variable_suite_constant | 55 ++++++++++ .../variable_block_statement_target | 75 +++++++++++++ .../variable_casebranch_enumdestructuring | 81 ++++++++++++++ .../location_link/variable_component_connect | 80 ++++++++++++++ .../variable_component_connect_as | 80 ++++++++++++++ .../location_link/variable_component_constant | 77 +++++++++++++ .../location_link/variable_component_function | 79 ++++++++++++++ .../location_link/variable_component_get | 79 ++++++++++++++ .../location_link/variable_component_property | 77 +++++++++++++ .../location_link/variable_component_state | 77 +++++++++++++ .../definition/location_link/variable_for | 75 +++++++++++++ .../location_link/variable_for_argument_stop | 43 ++++++++ .../location_link/variable_for_subject | 75 +++++++++++++ .../location_link/variable_function_argument | 73 +++++++++++++ .../variable_function_argument_stop | 29 +++++ .../location_link/variable_module_constant | 75 +++++++++++++ .../location_link/variable_nextcall_component | 79 ++++++++++++++ .../location_link/variable_nextcall_record | 94 ++++++++++++++++ .../location_link/variable_nextcall_store | 75 +++++++++++++ .../location_link/variable_nextcall_value | 78 ++++++++++++++ .../location_link/variable_provider_constant | 79 ++++++++++++++ .../location_link/variable_recordfield_key | 83 ++++++++++++++ .../location_link/variable_recordfield_value | 83 ++++++++++++++ .../location_link/variable_store_constant | 75 +++++++++++++ .../location_link/variable_store_function | 77 +++++++++++++ .../location_link/variable_store_get | 81 ++++++++++++++ .../location_link/variable_store_state | 79 ++++++++++++++ .../location_link/variable_suite_constant | 75 +++++++++++++ src/ls/definition/enum_id.cr | 41 +++++-- src/ls/definition/variable.cr | 101 ++++++++++++++++++ 56 files changed, 3697 insertions(+), 11 deletions(-) create mode 100644 spec/language_server/definition/location/variable_block_statement_target create mode 100644 spec/language_server/definition/location/variable_casebranch_enumdestructuring create mode 100644 spec/language_server/definition/location/variable_component_connect create mode 100644 spec/language_server/definition/location/variable_component_connect_as create mode 100644 spec/language_server/definition/location/variable_component_constant create mode 100644 spec/language_server/definition/location/variable_component_function create mode 100644 spec/language_server/definition/location/variable_component_get create mode 100644 spec/language_server/definition/location/variable_component_property create mode 100644 spec/language_server/definition/location/variable_component_state create mode 100644 spec/language_server/definition/location/variable_for create mode 100644 spec/language_server/definition/location/variable_for_argument_stop create mode 100644 spec/language_server/definition/location/variable_for_subject create mode 100644 spec/language_server/definition/location/variable_function_argument create mode 100644 spec/language_server/definition/location/variable_function_argument_stop create mode 100644 spec/language_server/definition/location/variable_module_constant create mode 100644 spec/language_server/definition/location/variable_nextcall_component create mode 100644 spec/language_server/definition/location/variable_nextcall_record create mode 100644 spec/language_server/definition/location/variable_nextcall_store create mode 100644 spec/language_server/definition/location/variable_nextcall_value create mode 100644 spec/language_server/definition/location/variable_provider_constant create mode 100644 spec/language_server/definition/location/variable_recordfield_key create mode 100644 spec/language_server/definition/location/variable_recordfield_value create mode 100644 spec/language_server/definition/location/variable_store_constant create mode 100644 spec/language_server/definition/location/variable_store_function create mode 100644 spec/language_server/definition/location/variable_store_get create mode 100644 spec/language_server/definition/location/variable_store_state create mode 100644 spec/language_server/definition/location/variable_suite_constant create mode 100644 spec/language_server/definition/location_link/variable_block_statement_target create mode 100644 spec/language_server/definition/location_link/variable_casebranch_enumdestructuring create mode 100644 spec/language_server/definition/location_link/variable_component_connect create mode 100644 spec/language_server/definition/location_link/variable_component_connect_as create mode 100644 spec/language_server/definition/location_link/variable_component_constant create mode 100644 spec/language_server/definition/location_link/variable_component_function create mode 100644 spec/language_server/definition/location_link/variable_component_get create mode 100644 spec/language_server/definition/location_link/variable_component_property create mode 100644 spec/language_server/definition/location_link/variable_component_state create mode 100644 spec/language_server/definition/location_link/variable_for create mode 100644 spec/language_server/definition/location_link/variable_for_argument_stop create mode 100644 spec/language_server/definition/location_link/variable_for_subject create mode 100644 spec/language_server/definition/location_link/variable_function_argument create mode 100644 spec/language_server/definition/location_link/variable_function_argument_stop create mode 100644 spec/language_server/definition/location_link/variable_module_constant create mode 100644 spec/language_server/definition/location_link/variable_nextcall_component create mode 100644 spec/language_server/definition/location_link/variable_nextcall_record create mode 100644 spec/language_server/definition/location_link/variable_nextcall_store create mode 100644 spec/language_server/definition/location_link/variable_nextcall_value create mode 100644 spec/language_server/definition/location_link/variable_provider_constant create mode 100644 spec/language_server/definition/location_link/variable_recordfield_key create mode 100644 spec/language_server/definition/location_link/variable_recordfield_value create mode 100644 spec/language_server/definition/location_link/variable_store_constant create mode 100644 spec/language_server/definition/location_link/variable_store_function create mode 100644 spec/language_server/definition/location_link/variable_store_get create mode 100644 spec/language_server/definition/location_link/variable_store_state create mode 100644 spec/language_server/definition/location_link/variable_suite_constant create mode 100644 src/ls/definition/variable.cr diff --git a/spec/language_server/definition/location/variable_block_statement_target b/spec/language_server/definition/location/variable_block_statement_target new file mode 100644 index 000000000..d75cc726f --- /dev/null +++ b/spec/language_server/definition/location/variable_block_statement_target @@ -0,0 +1,55 @@ +module Test { + fun upperCaseMint : String { + let test = + "Mint" + + String.toUpperCase(test) + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 23 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 2, + "character": 8 + }, + "end": { + "line": 2, + "character": 12 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_casebranch_enumdestructuring b/spec/language_server/definition/location/variable_casebranch_enumdestructuring new file mode 100644 index 000000000..e359f6743 --- /dev/null +++ b/spec/language_server/definition/location/variable_casebranch_enumdestructuring @@ -0,0 +1,61 @@ +enum Status { + Error + Ok(text : String) +} + +module Test { + fun toString (status : Status) : String { + case status { + Status::Ok(text) => text + Status::Error => "error" + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 8, + "character": 26 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 8, + "character": 17 + }, + "end": { + "line": 8, + "character": 21 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_connect b/spec/language_server/definition/location/variable_component_connect new file mode 100644 index 000000000..7d687e9ec --- /dev/null +++ b/spec/language_server/definition/location/variable_component_connect @@ -0,0 +1,60 @@ +component Test { + connect Theme exposing { primary } + + fun render : Html { +
+ <{ primary }> +
+ } +} +------------------------------------------------------------------file test.mint +store Theme { + state primary : String = "#00a0e8" +} +-----------------------------------------------------------------file store.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 27 + }, + "end": { + "line": 1, + "character": 34 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_connect_as b/spec/language_server/definition/location/variable_component_connect_as new file mode 100644 index 000000000..fcbfd2e46 --- /dev/null +++ b/spec/language_server/definition/location/variable_component_connect_as @@ -0,0 +1,60 @@ +component Test { + connect Theme exposing { primary as primaryColor } + + fun render : Html { +
+ <{ primaryColor }> +
+ } +} +------------------------------------------------------------------file test.mint +store Theme { + state primary : String = "#00a0e8" +} +-----------------------------------------------------------------file store.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 38 + }, + "end": { + "line": 1, + "character": 50 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_constant b/spec/language_server/definition/location/variable_component_constant new file mode 100644 index 000000000..60cb16028 --- /dev/null +++ b/spec/language_server/definition/location/variable_component_constant @@ -0,0 +1,57 @@ +component Test { + const TEXT = "Mint" + + fun render : Html { +
+ <{ TEXT }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_function b/spec/language_server/definition/location/variable_component_function new file mode 100644 index 000000000..5e2911d5d --- /dev/null +++ b/spec/language_server/definition/location/variable_component_function @@ -0,0 +1,59 @@ +component Test { + fun text : String { + "Mint" + } + + fun render : Html { +
+ <{ text() }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 7, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 10 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_get b/spec/language_server/definition/location/variable_component_get new file mode 100644 index 000000000..f0b6cec56 --- /dev/null +++ b/spec/language_server/definition/location/variable_component_get @@ -0,0 +1,59 @@ +component Test { + get text : String { + "Mint" + } + + fun render : Html { +
+ <{ text }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 7, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 10 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_property b/spec/language_server/definition/location/variable_component_property new file mode 100644 index 000000000..6be5ab434 --- /dev/null +++ b/spec/language_server/definition/location/variable_component_property @@ -0,0 +1,57 @@ +component Test { + property text : String = "Mint" + + fun render : Html { +
+ <{ text }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 11 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_component_state b/spec/language_server/definition/location/variable_component_state new file mode 100644 index 000000000..afce458b6 --- /dev/null +++ b/spec/language_server/definition/location/variable_component_state @@ -0,0 +1,57 @@ +component Test { + state text : String = "Mint" + + fun render : Html { +
+ <{ text }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_for b/spec/language_server/definition/location/variable_for new file mode 100644 index 000000000..7effba3f8 --- /dev/null +++ b/spec/language_server/definition/location/variable_for @@ -0,0 +1,55 @@ +module Test { + fun uppercase (values : Array(String)) : Array(String) { + for value of values { + String.toUpperCase(value) + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 3, + "character": 25 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 2, + "character": 8 + }, + "end": { + "line": 2, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_for_argument_stop b/spec/language_server/definition/location/variable_for_argument_stop new file mode 100644 index 000000000..359d8768a --- /dev/null +++ b/spec/language_server/definition/location/variable_for_argument_stop @@ -0,0 +1,43 @@ +module Test { + fun uppercase (value : String) : Array(String) { + for value of String.split(value, ",") { + String.toUpperCase(value) + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 2, + "character": 8 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": null, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_for_subject b/spec/language_server/definition/location/variable_for_subject new file mode 100644 index 000000000..003e00b12 --- /dev/null +++ b/spec/language_server/definition/location/variable_for_subject @@ -0,0 +1,55 @@ +module Test { + fun uppercase (value : String) : Array(String) { + for value of String.split(value, ",") { + String.toUpperCase(value) + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 2, + "character": 30 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 17 + }, + "end": { + "line": 1, + "character": 22 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_function_argument b/spec/language_server/definition/location/variable_function_argument new file mode 100644 index 000000000..aa54676df --- /dev/null +++ b/spec/language_server/definition/location/variable_function_argument @@ -0,0 +1,53 @@ +module Test { + fun toString (status : String) : String { + status + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 2, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 16 + }, + "end": { + "line": 1, + "character": 22 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_function_argument_stop b/spec/language_server/definition/location/variable_function_argument_stop new file mode 100644 index 000000000..58f659bae --- /dev/null +++ b/spec/language_server/definition/location/variable_function_argument_stop @@ -0,0 +1,29 @@ +store Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + next { text: text } + } +} +------------------------------------------------------------------file test.mint +{ + "jsonrpc": "2.0", + "id": 0, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 3, + "character": 12 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": null, + "id": 0 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_module_constant b/spec/language_server/definition/location/variable_module_constant new file mode 100644 index 000000000..0a6d47701 --- /dev/null +++ b/spec/language_server/definition/location/variable_module_constant @@ -0,0 +1,55 @@ +module Test { + const TITLE = "title" + + fun title : String { + TITLE + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_nextcall_component b/spec/language_server/definition/location/variable_nextcall_component new file mode 100644 index 000000000..0f8c2f191 --- /dev/null +++ b/spec/language_server/definition/location/variable_nextcall_component @@ -0,0 +1,59 @@ +component Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + next { text: text } + } + + fun render { +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 11 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_nextcall_record b/spec/language_server/definition/location/variable_nextcall_record new file mode 100644 index 000000000..c03cad4ba --- /dev/null +++ b/spec/language_server/definition/location/variable_nextcall_record @@ -0,0 +1,74 @@ +record Article { + id : Number, + description : String, + title : String +} + +store Test { + state article : Article = + { + id: 1, + description: "Mint Lang", + title: "Mint" + } + + fun load : Promise(Void) { + next + { + article: + { + id: 1, + description: "Mint Lang", + title: "Mint" + } + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 20, + "character": 12 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 2, + "character": 2 + }, + "end": { + "line": 2, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_nextcall_store b/spec/language_server/definition/location/variable_nextcall_store new file mode 100644 index 000000000..bb9adba8e --- /dev/null +++ b/spec/language_server/definition/location/variable_nextcall_store @@ -0,0 +1,55 @@ +store Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + next { text: text } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 11 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_nextcall_value b/spec/language_server/definition/location/variable_nextcall_value new file mode 100644 index 000000000..e6621c346 --- /dev/null +++ b/spec/language_server/definition/location/variable_nextcall_value @@ -0,0 +1,58 @@ +store Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + let newText = + "Mint" + + next { text: newText } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 7, + "character": 17 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 4, + "character": 8 + }, + "end": { + "line": 4, + "character": 15 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_provider_constant b/spec/language_server/definition/location/variable_provider_constant new file mode 100644 index 000000000..8e3ff65e5 --- /dev/null +++ b/spec/language_server/definition/location/variable_provider_constant @@ -0,0 +1,59 @@ +record Subscription { + a : Bool +} + +provider Provider : Subscription { + const TITLE = "title" + + fun title : String { + TITLE + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 8, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 5, + "character": 8 + }, + "end": { + "line": 5, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_recordfield_key b/spec/language_server/definition/location/variable_recordfield_key new file mode 100644 index 000000000..6bbf9f18d --- /dev/null +++ b/spec/language_server/definition/location/variable_recordfield_key @@ -0,0 +1,63 @@ +record Article { + id : Number, + description : String, + title : String +} + +module Test { + fun makeArticle (title : String) : Article { + { + id: 1, + description: "Mint Lang", + title: title + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 9, + "character": 6 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 4 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_recordfield_value b/spec/language_server/definition/location/variable_recordfield_value new file mode 100644 index 000000000..374e8cb6d --- /dev/null +++ b/spec/language_server/definition/location/variable_recordfield_value @@ -0,0 +1,63 @@ +record Article { + id : Number, + description : String, + title : String +} +---------------------------------------------------------------file article.mint +module Test { + fun makeArticle (title : String) : Article { + { + id: 1, + description: "Mint Lang", + title: title + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 13 + } + }, + "method": "textDocument/definition" +} +------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 19 + }, + "end": { + "line": 1, + "character": 24 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_store_constant b/spec/language_server/definition/location/variable_store_constant new file mode 100644 index 000000000..afcf3dd85 --- /dev/null +++ b/spec/language_server/definition/location/variable_store_constant @@ -0,0 +1,55 @@ +store Test { + const TITLE = "title" + + fun getTitle : String { + TITLE + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_store_function b/spec/language_server/definition/location/variable_store_function new file mode 100644 index 000000000..8f27064df --- /dev/null +++ b/spec/language_server/definition/location/variable_store_function @@ -0,0 +1,57 @@ +store Test { + fun load (text : String) : Promise(Void) { + Promise.never() + } + + fun dashboard : Promise(Void) { + await load("Mint") + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 6, + "character": 10 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 10 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_store_get b/spec/language_server/definition/location/variable_store_get new file mode 100644 index 000000000..ff9211007 --- /dev/null +++ b/spec/language_server/definition/location/variable_store_get @@ -0,0 +1,61 @@ +store Test { + get description : String { + "description" + } + + fun load (text : String) : Promise(Void) { + Promise.never() + } + + fun dashboard : Promise(Void) { + await load(description) + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 10, + "character": 15 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 17 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_store_state b/spec/language_server/definition/location/variable_store_state new file mode 100644 index 000000000..a70091112 --- /dev/null +++ b/spec/language_server/definition/location/variable_store_state @@ -0,0 +1,59 @@ +store Test { + state title : String = "title" + + fun load (text : String) : Promise(Void) { + Promise.never() + } + + fun dashboard : Promise(Void) { + await load(title) + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 8, + "character": 15 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location/variable_suite_constant b/spec/language_server/definition/location/variable_suite_constant new file mode 100644 index 000000000..26b459d6b --- /dev/null +++ b/spec/language_server/definition/location/variable_suite_constant @@ -0,0 +1,55 @@ +suite "Test" { + const TITLE = "title" + + test "it has a constant" { + TITLE == "title" + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": false + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "range": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "uri": "file://#{root_path}/test.mint" + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_block_statement_target b/spec/language_server/definition/location_link/variable_block_statement_target new file mode 100644 index 000000000..07e7d96b5 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_block_statement_target @@ -0,0 +1,75 @@ +module Test { + fun upperCaseMint : String { + let test = + "Mint" + + String.toUpperCase(test) + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 23 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 23 + }, + "end": { + "line": 5, + "character": 27 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 2, + "character": 4 + }, + "end": { + "line": 3, + "character": 12 + } + }, + "targetSelectionRange": { + "start": { + "line": 2, + "character": 8 + }, + "end": { + "line": 2, + "character": 12 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_casebranch_enumdestructuring b/spec/language_server/definition/location_link/variable_casebranch_enumdestructuring new file mode 100644 index 000000000..4eba5aceb --- /dev/null +++ b/spec/language_server/definition/location_link/variable_casebranch_enumdestructuring @@ -0,0 +1,81 @@ +enum Status { + Error + Ok(text : String) +} + +module Test { + fun toString (status : Status) : String { + case status { + Status::Ok(text) => text + Status::Error => "error" + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 8, + "character": 26 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 8, + "character": 26 + }, + "end": { + "line": 8, + "character": 30 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 8, + "character": 6 + }, + "end": { + "line": 8, + "character": 22 + } + }, + "targetSelectionRange": { + "start": { + "line": 8, + "character": 17 + }, + "end": { + "line": 8, + "character": 21 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_connect b/spec/language_server/definition/location_link/variable_component_connect new file mode 100644 index 000000000..1b2cef32f --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_connect @@ -0,0 +1,80 @@ +component Test { + connect Theme exposing { primary } + + fun render : Html { +
+ <{ primary }> +
+ } +} +------------------------------------------------------------------file test.mint +store Theme { + state primary : String = "#00a0e8" +} +-----------------------------------------------------------------file store.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 16 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 36 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 27 + }, + "end": { + "line": 1, + "character": 34 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_connect_as b/spec/language_server/definition/location_link/variable_component_connect_as new file mode 100644 index 000000000..6effa0999 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_connect_as @@ -0,0 +1,80 @@ +component Test { + connect Theme exposing { primary as primaryColor } + + fun render : Html { +
+ <{ primaryColor }> +
+ } +} +------------------------------------------------------------------file test.mint +store Theme { + state primary : String = "#00a0e8" +} +-----------------------------------------------------------------file store.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 21 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 52 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 38 + }, + "end": { + "line": 1, + "character": 50 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_constant b/spec/language_server/definition/location_link/variable_component_constant new file mode 100644 index 000000000..03022a727 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_constant @@ -0,0 +1,77 @@ +component Test { + const TEXT = "Mint" + + fun render : Html { +
+ <{ TEXT }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 13 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 21 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_function b/spec/language_server/definition/location_link/variable_component_function new file mode 100644 index 000000000..00126bf09 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_function @@ -0,0 +1,79 @@ +component Test { + fun text : String { + "Mint" + } + + fun render : Html { +
+ <{ text() }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 7, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 7, + "character": 9 + }, + "end": { + "line": 7, + "character": 13 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 3, + "character": 3 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 10 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_get b/spec/language_server/definition/location_link/variable_component_get new file mode 100644 index 000000000..1d5fcd4ca --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_get @@ -0,0 +1,79 @@ +component Test { + get text : String { + "Mint" + } + + fun render : Html { +
+ <{ text }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 7, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 7, + "character": 9 + }, + "end": { + "line": 7, + "character": 13 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 5, + "character": 2 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 10 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_property b/spec/language_server/definition/location_link/variable_component_property new file mode 100644 index 000000000..ce61b0357 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_property @@ -0,0 +1,77 @@ +component Test { + property text : String = "Mint" + + fun render : Html { +
+ <{ text }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 13 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 33 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 11 + }, + "end": { + "line": 1, + "character": 15 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_component_state b/spec/language_server/definition/location_link/variable_component_state new file mode 100644 index 000000000..e8705f482 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_component_state @@ -0,0 +1,77 @@ +component Test { + state text : String = "Mint" + + fun render : Html { +
+ <{ text }> +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 9 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 13 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 30 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_for b/spec/language_server/definition/location_link/variable_for new file mode 100644 index 000000000..abd703d41 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_for @@ -0,0 +1,75 @@ +module Test { + fun uppercase (values : Array(String)) : Array(String) { + for value of values { + String.toUpperCase(value) + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 3, + "character": 25 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 3, + "character": 25 + }, + "end": { + "line": 3, + "character": 30 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 2, + "character": 4 + }, + "end": { + "line": 5, + "character": 2 + } + }, + "targetSelectionRange": { + "start": { + "line": 2, + "character": 8 + }, + "end": { + "line": 2, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_for_argument_stop b/spec/language_server/definition/location_link/variable_for_argument_stop new file mode 100644 index 000000000..951f304ff --- /dev/null +++ b/spec/language_server/definition/location_link/variable_for_argument_stop @@ -0,0 +1,43 @@ +module Test { + fun uppercase (value : String) : Array(String) { + for value of String.split(value, ",") { + String.toUpperCase(value) + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 2, + "character": 8 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": null, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_for_subject b/spec/language_server/definition/location_link/variable_for_subject new file mode 100644 index 000000000..3c41c018e --- /dev/null +++ b/spec/language_server/definition/location_link/variable_for_subject @@ -0,0 +1,75 @@ +module Test { + fun uppercase (value : String) : Array(String) { + for value of String.split(value, ",") { + String.toUpperCase(value) + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 2, + "character": 30 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 2, + "character": 30 + }, + "end": { + "line": 2, + "character": 35 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 17 + }, + "end": { + "line": 1, + "character": 31 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 17 + }, + "end": { + "line": 1, + "character": 22 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_function_argument b/spec/language_server/definition/location_link/variable_function_argument new file mode 100644 index 000000000..78b9f72a3 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_function_argument @@ -0,0 +1,73 @@ +module Test { + fun toString (status : String) : String { + status + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 2, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 2, + "character": 4 + }, + "end": { + "line": 2, + "character": 10 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 16 + }, + "end": { + "line": 1, + "character": 31 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 16 + }, + "end": { + "line": 1, + "character": 22 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_function_argument_stop b/spec/language_server/definition/location_link/variable_function_argument_stop new file mode 100644 index 000000000..58f659bae --- /dev/null +++ b/spec/language_server/definition/location_link/variable_function_argument_stop @@ -0,0 +1,29 @@ +store Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + next { text: text } + } +} +------------------------------------------------------------------file test.mint +{ + "jsonrpc": "2.0", + "id": 0, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 3, + "character": 12 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": null, + "id": 0 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_module_constant b/spec/language_server/definition/location_link/variable_module_constant new file mode 100644 index 000000000..f9adb0c1b --- /dev/null +++ b/spec/language_server/definition/location_link/variable_module_constant @@ -0,0 +1,75 @@ +module Test { + const TITLE = "title" + + fun title : String { + TITLE + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 4, + "character": 4 + }, + "end": { + "line": 4, + "character": 9 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 23 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_nextcall_component b/spec/language_server/definition/location_link/variable_nextcall_component new file mode 100644 index 000000000..65bb1e6ff --- /dev/null +++ b/spec/language_server/definition/location_link/variable_nextcall_component @@ -0,0 +1,79 @@ +component Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + next { text: text } + } + + fun render { +
+ } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 11 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 4, + "character": 11 + }, + "end": { + "line": 4, + "character": 15 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 26 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_nextcall_record b/spec/language_server/definition/location_link/variable_nextcall_record new file mode 100644 index 000000000..b8a376929 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_nextcall_record @@ -0,0 +1,94 @@ +record Article { + id : Number, + description : String, + title : String +} + +store Test { + state article : Article = + { + id: 1, + description: "Mint Lang", + title: "Mint" + } + + fun load : Promise(Void) { + next + { + article: + { + id: 1, + description: "Mint Lang", + title: "Mint" + } + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 20, + "character": 12 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 20, + "character": 12 + }, + "end": { + "line": 20, + "character": 23 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 2, + "character": 2 + }, + "end": { + "line": 2, + "character": 22 + } + }, + "targetSelectionRange": { + "start": { + "line": 2, + "character": 2 + }, + "end": { + "line": 2, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_nextcall_store b/spec/language_server/definition/location_link/variable_nextcall_store new file mode 100644 index 000000000..61de09f1c --- /dev/null +++ b/spec/language_server/definition/location_link/variable_nextcall_store @@ -0,0 +1,75 @@ +store Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + next { text: text } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 11 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 4, + "character": 11 + }, + "end": { + "line": 4, + "character": 15 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 26 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 12 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_nextcall_value b/spec/language_server/definition/location_link/variable_nextcall_value new file mode 100644 index 000000000..bd00bd2e9 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_nextcall_value @@ -0,0 +1,78 @@ +store Test { + state text : String = "" + + fun load (text : String) : Promise(Void) { + let newText = + "Mint" + + next { text: newText } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 7, + "character": 17 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 7, + "character": 17 + }, + "end": { + "line": 7, + "character": 24 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 4, + "character": 4 + }, + "end": { + "line": 5, + "character": 12 + } + }, + "targetSelectionRange": { + "start": { + "line": 4, + "character": 8 + }, + "end": { + "line": 4, + "character": 15 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_provider_constant b/spec/language_server/definition/location_link/variable_provider_constant new file mode 100644 index 000000000..4f18c8a0a --- /dev/null +++ b/spec/language_server/definition/location_link/variable_provider_constant @@ -0,0 +1,79 @@ +record Subscription { + a : Bool +} + +provider Provider : Subscription { + const TITLE = "title" + + fun title : String { + TITLE + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 8, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 8, + "character": 4 + }, + "end": { + "line": 8, + "character": 9 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 5, + "character": 23 + } + }, + "targetSelectionRange": { + "start": { + "line": 5, + "character": 8 + }, + "end": { + "line": 5, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_recordfield_key b/spec/language_server/definition/location_link/variable_recordfield_key new file mode 100644 index 000000000..5846bfacb --- /dev/null +++ b/spec/language_server/definition/location_link/variable_recordfield_key @@ -0,0 +1,83 @@ +record Article { + id : Number, + description : String, + title : String +} + +module Test { + fun makeArticle (title : String) : Article { + { + id: 1, + description: "Mint Lang", + title: title + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 9, + "character": 6 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 9, + "character": 6 + }, + "end": { + "line": 9, + "character": 8 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 4 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_recordfield_value b/spec/language_server/definition/location_link/variable_recordfield_value new file mode 100644 index 000000000..c80653428 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_recordfield_value @@ -0,0 +1,83 @@ +record Article { + id : Number, + description : String, + title : String +} +---------------------------------------------------------------file article.mint +module Test { + fun makeArticle (title : String) : Article { + { + id: 1, + description: "Mint Lang", + title: title + } + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 5, + "character": 13 + } + }, + "method": "textDocument/definition" +} +------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 5, + "character": 13 + }, + "end": { + "line": 5, + "character": 18 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 19 + }, + "end": { + "line": 1, + "character": 33 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 19 + }, + "end": { + "line": 1, + "character": 24 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_store_constant b/spec/language_server/definition/location_link/variable_store_constant new file mode 100644 index 000000000..30d3dd4f7 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_store_constant @@ -0,0 +1,75 @@ +store Test { + const TITLE = "title" + + fun getTitle : String { + TITLE + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 4, + "character": 4 + }, + "end": { + "line": 4, + "character": 9 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 23 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_store_function b/spec/language_server/definition/location_link/variable_store_function new file mode 100644 index 000000000..fa4c95c04 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_store_function @@ -0,0 +1,77 @@ +store Test { + fun load (text : String) : Promise(Void) { + Promise.never() + } + + fun dashboard : Promise(Void) { + await load("Mint") + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 6, + "character": 10 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 6, + "character": 10 + }, + "end": { + "line": 6, + "character": 14 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 3, + "character": 3 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 10 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_store_get b/spec/language_server/definition/location_link/variable_store_get new file mode 100644 index 000000000..853222a93 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_store_get @@ -0,0 +1,81 @@ +store Test { + get description : String { + "description" + } + + fun load (text : String) : Promise(Void) { + Promise.never() + } + + fun dashboard : Promise(Void) { + await load(description) + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 10, + "character": 15 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 10, + "character": 15 + }, + "end": { + "line": 10, + "character": 26 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 5, + "character": 2 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 6 + }, + "end": { + "line": 1, + "character": 17 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_store_state b/spec/language_server/definition/location_link/variable_store_state new file mode 100644 index 000000000..9c05f619f --- /dev/null +++ b/spec/language_server/definition/location_link/variable_store_state @@ -0,0 +1,79 @@ +store Test { + state title : String = "title" + + fun load (text : String) : Promise(Void) { + Promise.never() + } + + fun dashboard : Promise(Void) { + await load(title) + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 8, + "character": 15 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 8, + "character": 15 + }, + "end": { + "line": 8, + "character": 20 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 32 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/spec/language_server/definition/location_link/variable_suite_constant b/spec/language_server/definition/location_link/variable_suite_constant new file mode 100644 index 000000000..cdc6aa815 --- /dev/null +++ b/spec/language_server/definition/location_link/variable_suite_constant @@ -0,0 +1,75 @@ +suite "Test" { + const TITLE = "title" + + test "it has a constant" { + TITLE == "title" + } +} +------------------------------------------------------------------file test.mint +{ + "id": 0, + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "definition": { + "linkSupport": true + } + } + } + } +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "id": 1, + "params": { + "textDocument": { + "uri": "file://#{root_path}/test.mint" + }, + "position": { + "line": 4, + "character": 4 + } + }, + "method": "textDocument/definition" +} +-------------------------------------------------------------------------request +{ + "jsonrpc": "2.0", + "result": { + "originSelectionRange": { + "start": { + "line": 4, + "character": 4 + }, + "end": { + "line": 4, + "character": 9 + } + }, + "targetUri": "file://#{root_path}/test.mint", + "targetRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 23 + } + }, + "targetSelectionRange": { + "start": { + "line": 1, + "character": 8 + }, + "end": { + "line": 1, + "character": 13 + } + } + }, + "id": 1 +} +------------------------------------------------------------------------response diff --git a/src/ls/definition/enum_id.cr b/src/ls/definition/enum_id.cr index 9be315861..0b4c91b91 100644 --- a/src/ls/definition/enum_id.cr +++ b/src/ls/definition/enum_id.cr @@ -2,20 +2,39 @@ module Mint module LS class Definition < LSP::RequestMessage def definition(node : Ast::EnumId, server : Server, workspace : Workspace, stack : Array(Ast::Node)) - return unless name = node.name - return unless enum_node = - workspace.ast.enums.find(&.name.value.==(name.value)) + name = node.name - return if Core.ast.enums.includes?(enum_node) + # When `.name` is nil the node is used as a CONSTANT + if name.nil? + stack.each do |parent| + case parent + when Ast::Component, + Ast::Store, + Ast::Suite, + Ast::Module, + Ast::Provider + parent.constants.each do |constant| + if node.option.value == constant.name.value + return location_link server, node.option, constant.name, constant + end + end + end + end + else + return unless enum_node = + workspace.ast.enums.find(&.name.value.==(name.value)) - case - when cursor_intersects?(name) - location_link server, name, enum_node.name, enum_node - when cursor_intersects?(node.option) - return unless option = - enum_node.try &.options.find(&.value.value.==(node.option.value)) + return if Core.ast.enums.includes?(enum_node) - location_link server, node.option, option.value, option + case + when cursor_intersects?(name) + location_link server, name, enum_node.name, enum_node + when cursor_intersects?(node.option) + return unless option = + enum_node.try &.options.find(&.value.value.==(node.option.value)) + + location_link server, node.option, option.value, option + end end end end diff --git a/src/ls/definition/variable.cr b/src/ls/definition/variable.cr new file mode 100644 index 000000000..8894f9bb8 --- /dev/null +++ b/src/ls/definition/variable.cr @@ -0,0 +1,101 @@ +module Mint + module LS + class Definition < LSP::RequestMessage + def definition(node : Ast::Variable, server : Server, workspace : Workspace, stack : Array(Ast::Node)) + lookup = workspace.type_checker.variables[node]? + + if lookup + variable_lookup_parent(node, lookup[1], server, workspace) || + variable_connect(node, lookup[2], server) || + variable_lookup(node, lookup[0], server) + else + variable_record_key(node, server, workspace, stack) || + variable_next_key(node, server, workspace, stack) + end + end + + def variable_connect(node : Ast::Variable, parents : Array(TypeChecker::Scope::Node), server : Server) + # Check to see if this variable is defined as an Ast::ConnectVariable + # as the `.variables` cache links directly to the stores state/function etc + return unless component = parents.select(Ast::Component).first? + + component.connects.each do |connect| + connect.keys.each do |key| + variable = key.name || key.variable + + if variable.value == node.value + return location_link server, node, variable, connect + end + end + end + end + + def variable_lookup_parent(node : Ast::Variable, target : TypeChecker::Scope::Node, server : Server, workspace : Workspace) + case target + when Tuple(String, TypeChecker::Checkable, Ast::Node) + case variable = target[2] + when Ast::Variable + # For some variables in the .variables` cache, we only have access to the + # target Ast::Variable and not its containing node, so we must search for it + return unless parent = workspace + .ast + .nodes + .select { |other| other.is_a?(Ast::EnumDestructuring) || other.is_a?(Ast::Statement) || other.is_a?(Ast::For) } + .select(&.input.file.==(variable.input.file)) + .find { |other| other.from < variable.from && other.to > variable.to } + + location_link server, node, variable, parent + end + end + end + + def variable_lookup(node : Ast::Variable, target : Ast::Node | TypeChecker::Checkable, server : Server) + case item = target + when Ast::Node + name = case item + when Ast::Property, + Ast::Constant, + Ast::Function, + Ast::State, + Ast::Get, + Ast::Argument + item.name + else + item + end + + location_link server, node, name, item + end + end + + def variable_record_key(node : Ast::Variable, server : Server, workspace : Workspace, stack : Array(Ast::Node)) + case field = stack[1]? + when Ast::RecordField + return unless record_name = workspace.type_checker.record_field_lookup[field]? + + return unless record_definition_field = workspace + .ast + .records + .find(&.name.value.==(record_name)) + .try(&.fields.find(&.key.value.==(node.value))) + + location_link server, node, record_definition_field.key, record_definition_field + end + end + + def variable_next_key(node : Ast::Variable, server : Server, workspace : Workspace, stack : Array(Ast::Node)) + case next_call = stack[3]? + when Ast::NextCall + return unless parent = workspace.type_checker.lookups[next_call] + + return unless state = case parent + when Ast::Provider, Ast::Component, Ast::Store + parent.states.find(&.name.value.==(node.value)) + end + + location_link server, node, state.name, state + end + end + end + end +end