From ab7f9ff33e74aa466a607981ed8bb8d5577bdefa Mon Sep 17 00:00:00 2001 From: Hans Krutzer Date: Mon, 26 Jul 2021 16:23:00 +0200 Subject: [PATCH] [Elixir] Update syntax --- Elixir/Elixir (EEx).sublime-syntax | 80 +- Elixir/Elixir.sublime-syntax | 2337 +++++++++++------ Elixir/HTML (EEx).sublime-syntax | 23 +- Elixir/PCRE (Erlang).sublime-syntax | 480 ++++ ...egular Expressions (Elixir).sublime-syntax | 480 ---- Elixir/SQL (Elixir).sublime-syntax | 106 +- Elixir/VERSION | 2 +- 7 files changed, 2125 insertions(+), 1383 deletions(-) create mode 100644 Elixir/PCRE (Erlang).sublime-syntax delete mode 100644 Elixir/Regular Expressions (Elixir).sublime-syntax diff --git a/Elixir/Elixir (EEx).sublime-syntax b/Elixir/Elixir (EEx).sublime-syntax index 42de4b822e..aa3b396051 100644 --- a/Elixir/Elixir (EEx).sublime-syntax +++ b/Elixir/Elixir (EEx).sublime-syntax @@ -1,61 +1,33 @@ %YAML 1.2 --- -# TODO: uncomment when on separate ST4 branch in the future. -# version: 2 +version: 2 name: Elixir (EEx) -file_extensions: - - ex.eex - - exs.eex +file_extensions: [ex.eex, exs.eex] first_line_match: ^#\s*exs?\.eex -scope: source.ex.eex +scope: source.elixir.eex extends: Elixir.sublime-syntax contexts: - main: - - include: core_syntax - - # Commented out because ST3 complains. - # TODO: uncomment when on separate ST4 branch in the future. - - # # prototype: - # core_syntax: - # - meta_prepend: true - # - include: eex - - # defmodule_1st_argument: - # - meta_prepend: true - # - include: eex - - # # FIXME: doesn't highlight inside doc comments yet. - # markdown_comment: - # - meta_prepend: true - # - include: eex - - # simple_string: - # - meta_prepend: true - # - include: eex - - # block_end_pop: - # - meta_prepend: true - # - include: eex - - # module_function_call_pop: - # - meta_prepend: true - # - include: eex - - # identifier_operator_call_pop: - # - meta_prepend: true - # - include: eex - - # dot_accessor: - # - meta_prepend: true - # - include: eex - - # eex: - # - match: <%(?>%=?|[=/|]?) - # scope: keyword.other.ex.eex punctuation.section.embedded.begin.ex.eex - # push: - # - match: '%>' - # scope: text.html.ex.eex keyword.other.ex.eex punctuation.section.embedded.end.ex.eex - # pop: true - # - include: core_syntax + # Use prototype to work around the "context sanity limit" error. + # An issue that remains is that "<% ... %>" embeds are not independent + # of the surroundings. + prototype: + - include: eex_begin_tag + - include: eex_end_tag + + eex_begin_tag: + - match: (<)(%(?>%=?|[=/|]?)) + captures: + 1: punctuation.section.embedded.begin.ex.eex + 2: entity.name.tag.ex.eex + # # NB: causes "context sanity limit" error. + # push: core_syntax + # with_prototype: + # - match: (?=%>|<%) + # pop: true + + eex_end_tag: + - match: \s*(%)(>) + captures: + 1: entity.name.tag.ex.eex + 2: punctuation.section.embedded.end.ex.eex diff --git a/Elixir/Elixir.sublime-syntax b/Elixir/Elixir.sublime-syntax index 79b44e3ba6..be01bef16a 100644 --- a/Elixir/Elixir.sublime-syntax +++ b/Elixir/Elixir.sublime-syntax @@ -1,78 +1,185 @@ %YAML 1.2 --- +version: 2 # http://www.sublimetext.com/docs/3/syntax.html name: Elixir -file_extensions: - - ex - - exs -first_line_match: ^#!\s*/.*\b(?:elixirc?|iex) scope: source.elixir +file_extensions: [ex, exs] +first_line_match: ^#!\s*/.*\b(?:elixirc?|iex) +authors: + # Contributed several times and later on finished a complete rewrite with lots of new features. + - Aziz Köksal + + # Transformed elixir-tmbundle to a sublime-syntax file with additional improvements. + - Po Chen + + # Created elixir-tmbundle by basing off of the Textmate bundle for Ruby (by James Edward Gray II). + - José Valim variables: - module_name: '[A-Z][a-zA-Z\d_]*\b' - atom_id_suffix: '[\w@]*[?!]?' + module_name: '[A-Z][a-zA-Z\d_]*+' + atom_id_suffix: '[\w@]*+[?!]?+' atom_id: (?>[[:alpha:]_]{{atom_id_suffix}}) - identifier: (?>[[:lower:]_]\w*[?!]?) - not_id_key_suffix: \b(?!{{atom_id_suffix}}:(?!:)|[?!]) + identifier: (?>[[:lower:]_]\w*+[?!]?+) + no_id_key_suffix: (?=\b(?!{{atom_id_suffix}}:(?!:)|[?!])|(?<=[?!])(?!:(?!:))) + no_key_suffix: (?!{{atom_id_suffix}}:(?!:)) + no_colon_suffix: (?![:'"[:alpha:]]) # NB: the keywords are ordered by number of occurrences in the compiler source code of Elixir. - closing_keywords: (?>end|else|after|rescue|catch){{not_id_key_suffix}} - closing_token: (?>[,;)}\]#]|%>|$|{{closing_keywords}}) - binary_operator: (?>(?>?|<~>|[*=/|\\>.]|<(?!<[^<~]|%=)|\^\^|&&|(?\s]|::(?!:)|$|!=|(?>and|or|when|in|not\s+in){{not_id_key_suffix}}) - is_argument_token: (?!\s*(?>{{closing_token}}|{{binary_operator}})) - has_arguments: (?=\(|{{is_argument_token}}) - no_suffix_then_arguments: \b(?![?!]){{has_arguments}} - - operators: (?>=~|={1,3}|!={0,2}|<<<|>>>|~~~|::|<~>|>?||/|\\\\|\*|\.{1,3}|[<>]=|---|[<-][->]?|>|&{1,3}|\+{1,3}|\|{1,3}|@|\^{3}|\^) - - special_atom: (?><<>>|%?{}|%|\[\]|\^\^\^?) - atom_symbol: (?>{{atom_id}}|{{special_atom}}|{{operators}}) + # Stats can be produced using `Makeup.Lexers.ElixirLexer.lex()`. + # To be more accurate "scripts/count_tokens_by_scope.py" can be used. + # Run `cat lib/**/*.ex > all.ex` to join all source files into a single file. + closing_keyword: (?>end|else|after|rescue|catch){{no_id_key_suffix}} + closing_token: (?>[)}\];#]|>>(?!>)|%>|$|{{closing_keyword}}) + + binary_operator: | + (?x)(?> + (?>? | <~> | [*=/|\\>] | \.(?!\.\.) | <(?!<[^<~] | %=) + | \^{3} | && | (?\s] | ::(?!:) | $ | != + | (?>when|and|in|or|not\s+in){{no_id_key_suffix}} + ) + + operator: | + (?x)(?> + =~ | ={1,3} | @ | | (?!<%=)[<-](?![~=] | < | --)[->]? | \|{1,3} | :: + | &{1,3} | \+{1,3} | \\\\ | \^{3} | \^ | \.{1,3} | !={0,2} | [<>]= | \* + | >>> | >(?!>) | // | / | <<< | ~~~ | <~> | >? | --- + ) + + has_arguments: | + (?x)(?= + \( + | \s* (?!\?:) (?>{{operator}}: {{no_colon_suffix}} | \\(?!\\)) + | (?! \s* (?>, | {{closing_token}} | {{binary_operator}}) ) + ) + no_suffix_then_arguments: '{{no_id_key_suffix}}{{has_arguments}}' + + atom_symbol: (?>{{atom_id}}|<<>>|\.\.//|{{operator}}|%?{}|%) + member: (?>{{identifier}}|{{operator}}|{}) + + # This regex is used to recognize Elixir inside Markdown comments. + # The rules have been commented out as the recognition wasn't reliable enough. + # It was incomplete, maybe distracting and speed may have been affected, too. + # It can be re-enabled by uncommenting all lines containing "{{is_markdown_elixir}}". + is_markdown_elixir: | + (?x) + ```\s*elixir\b + | (?<=\s{4})(?> + (?>iex|\.{3}).*?> + | (?> + def(?>module|impl|macro|guard|struct|delegate|protocol|p)? + | (?#if|for|)unless|case|cond|with|try|receive|quote|unquote(?:_splicing)? + ){{no_id_key_suffix}} + (?>\(|.+\bdo\b) + ) contexts: main: - include: core_syntax - # NB: rules for type matching can only work when only they can match the union operator. - # We can still highlight "|" by including it here. - - include: union_operator + + prototype: + # NB: rules in the prototype context will always match before any other. + - include: merge_conflict # Merge conflict markers can occur anywhere. + # Commented out: see "is_markdown_elixir" above. + # - include: iex_dots # iex prompt continuation: ...> core_syntax: - - include: atom_keyword - - include: atom_symbol - - include: numeric - - include: string - - include: def_blocks - - include: do_block - - include: fn_block - - include: tuple - - include: parens - - include: list - - include: map - - include: merge_conflict - - include: operator - - include: capture - - include: dot_accessor - - include: alias - - include: elixir_keywords - - include: elixir_functions - - include: special_form - - include: sql_fragment - - include: built_ins - - include: module_attribute - - include: modules_or_identifiers_or_calls - - include: comment - - include: char_literal + # The rules are approximately ordered by their likeliness of occurrence + # in Elixir's and Phoenix's source code. + # Some rules have to come before others in any case, like atom_keyword for example. + - include: atom_keyword # key: + - include: def_blocks # def f(a, b, c) + - include: elixir_keywords # when, true, and, else ... + - include: built_ins # is_binary(x) + - include: alias # alias M + - include: fn_block # fn a -> a end + - include: elixir_functions # if, case, with + - include: special_form # __MODULE__, __ENV__ + - include: do_block # do end + - include: sql_or_fragment # sql("SELECT * FROM t"); fragment("meta->>key") + - include: modules_or_ids_or_calls # Module, id, M.f(), x.y() + - include: comma_and_skip_ws # , + - include: operator # a == b + - include: atom_symbol # :key + - include: string # "x", << x >>, ~w[x]a, ~r/x/ + - include: dot_accessor # .x + - include: tuple # {a, b, c} + - include: item_access # x[y] + - include: list # [a, b, c] + - include: module_attribute # @type t, @spec s, @doc "D" + - include: numeric # 1, 2.0e3, 0xFF, 0b10, 0o7 + - include: map # %{}, %M{} + - include: comment # # ... + - include: capture # &Module.f/1, &(&1<>&2) + - include: char_literal # ?x + - include: paren # (x) + - include: line_continuation # \ - fn_block: - - match: fn{{not_id_key_suffix}} - scope: keyword.declaration.function.elixir punctuation.section.block.begin.elixir + merge_conflict: + - match: ^(?=<{7}|={7}|>{7}) push: - # - meta_scope: meta.function.parameters.elixir - - match: (?=->|when{{not_id_key_suffix}}) - set: - - include: block_end_pop - - include: core_syntax - - include: block_end_pop - - include: fn_parameters + - clear_scopes: true + - match: (?>(<+)(.*\n)|(=+)(.*\n)|(>+)(.*\n)) + scope: text.git.conflict + captures: + 1: punctuation.section.block.begin.git.conflict + 2: comment.line.current.git.conflict + 3: punctuation.section.block.middle.git.conflict + 4: comment.line.middle.git.conflict + 5: punctuation.section.block.end.git.conflict + 6: comment.line.incoming.git.conflict + pop: true + + ## Functions + + fn_block: + - match: fn{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.other.fn.elixir + push: [fn_block_end_pop, arrow_clauses_body_pop, fn_single_body_or_pop] + + fn_single_body_or_pop: + - match: (?=->) + set: + - match: (?=end{{no_id_key_suffix}}) + pop: true + - include: core_syntax + - include: if_non_space_or_eol_pop + + fn_block_end_pop: + - include: block_end_pop + - include: if_closing_token_pop + - include: core_syntax + + arrow_clauses_body_pop: + - match: (?=\S) + set: + - match: (?=->|when{{no_id_key_suffix}}) + push: inlined_core_syntax_pop + # NB: no default parameters in arrow clauses + - match: \\\\(?!:{{no_colon_suffix}}) + scope: keyword.operator.default.elixir invalid.illegal.default-operator.elixir + - include: parameters_or_if_closing_pop + - match: $ + set: + - include: if_closing_token_pop + - match: ^(\s*)(?=[^#\s]) + push: [indented_core_syntax_pop, params_until_arrow_pop] + - include: core_syntax + + inlined_core_syntax_pop: + - match: (?=;) + pop: true + - include: core_syntax_or_if_closing_pop + + params_until_arrow_pop: + - match: (?=->|when{{no_id_key_suffix}}) + pop: true + - include: parameters_or_if_closing_pop + + indented_core_syntax_pop: + - match: ^(?=\1[^#\s]|(?!\1)(?!\s*(#|$))) + pop: true + - include: core_syntax_or_if_closing_pop function_header_pop: - match: \( @@ -91,24 +198,29 @@ contexts: push: function_header_args_pop function_header_args_pop: - - match: unquote(?=\s*\() - scope: keyword.other.elixir - set: [function_params_or_eol_pop, arguments_pop] + - match: (?={{atom_symbol}}:(?!:))|(?<=[^:]:) + set: arguments_ws_rest_pop - - include: do_block_pop - - include: elixir_keywords + - match: (?=unquote\(\s*:(?!:(?!:))) + set: [function_params_or_eol_pop, function_atom_name_pop, unquote_pop] + + - match: (?=unquote(?:_splicing)?\() + set: [function_params_or_eol_pop, arguments_pop, unquote_pop] - - match: '{{identifier}}(?=\s){{is_argument_token}}' - comment: free-form header, e.g. "def func a, b do {a, b} end" + - match: not{{no_suffix_then_arguments}} scope: entity.name.function.elixir - set: - - match: (?={{atom_symbol}}:(?!:)) - comment: break at first atom key (usually "do:") - set: function_post_params_pop - - include: arg_comma_and_skip_ws - - include: function_ws_params_pop + set: function_free_form_header_pop - - match: '{{identifier}}(?=\s*([(),;]|do{{not_id_key_suffix}}|$))' + - include: atom_keyword + - include: func_do_block_pop + - include: block_or_keyword + - include: paren_param + + - match: '{{identifier}}(?=\s){{has_arguments}}' + scope: entity.name.function.elixir + set: function_free_form_header_pop + + - match: '{{identifier}}(?=\s*([(),;]|do{{no_id_key_suffix}}|$))' comment: e.g. "def func," OR "def func(a, b)" OR "def func do" OR "def func" scope: entity.name.function.elixir set: function_params_or_eol_pop @@ -117,28 +229,48 @@ contexts: set: - match: | (?x) - (?>(_{{identifier}}?)|({{identifier}}))? + (?>((?=_)(?){{identifier}})|({{identifier}}))? \s* - ({{operators}}|(?>in|and|or){{not_id_key_suffix}}) + ({{operator}}|(?>and|in|or){{no_id_key_suffix}}) comment: e.g. x && y captures: 1: variable.parameter.unused.elixir 2: variable.parameter.elixir 3: entity.name.function.elixir set: - - match: (?=,) - set: function_post_params_pop - - include: function_ws_params_pop - - include: if_closing_tokens_pop - - include: parens - - include: parameters + - match: (?=,|when{{no_id_key_suffix}}) + set: arguments_ws_rest_pop + - include: func_do_block_pop + - include: if_ws_closing_token_or_eol_pop + - include: paren_param + - include: parameters_or_if_closing_pop + - include: if_non_space_pop + - include: if_ws_closing_token_or_eol_pop + - include: paren + - match: \{(?!}:(?!:(?!:))) + scope: entity.name.function.elixir + set: + - meta_scope: meta.function.parameters.elixir + - match: \} + scope: entity.name.function.elixir + set: arguments_ws_rest_pop + - include: parameters_or_if_closing_pop + - include: parameters_or_if_closing_pop - include: function_params_or_eol_pop + function_free_form_header_pop: + # E.g.: "def func a, b do {a, b} end" + - match: (?={{atom_symbol}}:(?!:))|(?<=[^:]:)|(?=when{{no_id_key_suffix}}) + comment: break at first atom key (usually "do:") + set: arguments_ws_rest_pop + - include: arg_comma_and_skip_ws + - include: function_ws_params_pop + function_ws_params_pop: - - include: do_block_pop - - include: if_closing_tokens_pop - - include: parens - - include: parameters + - include: func_do_block_pop + - include: if_ws_closing_token_or_eol_pop + - include: paren + - include: parameters_or_if_closing_pop - include: if_non_space_pop function_params_or_eol_pop: @@ -148,56 +280,81 @@ contexts: - meta_scope: meta.function.parameters.elixir - match: \) scope: punctuation.definition.parameters.end.elixir - set: function_post_params_pop - - include: parameters + set: function_body_pop + - include: parameters_or_if_closing_pop - match: (?=,) - set: function_post_params_pop - - include: do_block_pop - - include: if_closing_tokens_pop + set: arguments_ws_rest_pop + - include: func_do_block_pop + - include: if_ws_closing_token_or_eol_pop - include: if_non_space_pop - function_post_params_pop: - - include: arg_comma_and_skip_ws - - include: do_block_pop - - include: if_closing_tokens_pop - - include: core_syntax + function_body_pop: + - include: func_do_block_pop + - include: arguments_ws_rest_pop - ## Parameters + function_atom_name_pop: + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_closing_pop + - match: ':' + scope: constant.other.symbol.elixir punctuation.definition.constant.begin.elixir + push: + - match: (?=<%)|{{atom_symbol}} + scope: constant.other.symbol.elixir entity.name.function.elixir + pop: true + - match: (["']) + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: constant.other.symbol.quoted.elixir + - match: \1 + scope: punctuation.definition.constant.end.elixir + pop: true + - include: interpolated_elixir + - match: \\?.|\n + scope: entity.name.function.elixir + - include: if_empty_pop + - include: core_syntax - fn_parameters: - # NB: no default parameters in fn blocks - - match: \\\\(?!:) - scope: keyword.operator.other.elixir invalid.illegal.default-operator.elixir - - include: parameters + ## Parameters parameters: - include: block_or_keyword - - match: __MODULE__{{not_id_key_suffix}} - scope: variable.language.special-form.elixir + - include: special_form + - match: (?=\^\s*{{identifier}}{{no_key_suffix}}(?!\s*\.(?!\.))(?!{{has_arguments}})) + push: + - include: operator + - include: identifier_pop - match: | (?x) - (?>((?[,;)}\]=|\\]|\.\.|::|->|(?>when|in|do){{not_id_key_suffix}}|$)) + (?>((?=_)(?){{identifier}})|({{identifier}})) + (?=\s*(?>[,;)}\]=|\\]|\.\.|::|->|<-|<>|>>|(?>when|do){{no_id_key_suffix}}|$)) captures: 1: variable.parameter.unused.elixir 2: variable.parameter.elixir - - match: \,(?=\s*\)) - scope: invalid.illegal.separator.elixir - - include: comma_and_skip_ws - - match: \\\\(?!:) - scope: keyword.operator.other.elixir + - match: (?=\\\\(?!:{{no_colon_suffix}})|<-) push: - - match: (?=[,)]|(?>when|do){{not_id_key_suffix}}) + - match: (?=,|(?>when|do){{no_id_key_suffix}}|do:(?!:)) pop: true - - include: core_syntax - - match: (?=\() - set: paren_param_pop - - include: tuple_param + - include: core_syntax_or_if_closing_pop + - include: paren_param - include: list_param + - include: tuple_param - include: map_param - include: binary_string_param + # Use last_id_argument here to avoid matching `@attr do` as an entity symbol. + - include: last_id_argument - include: core_syntax + parameters_or_if_closing_pop: + - include: if_closing_token_pop + - include: parameters + + paren_param: + - match: (?=\() + push: paren_param_pop + paren_param_pop: - match: \( scope: punctuation.definition.parameters.begin.elixir @@ -206,8 +363,7 @@ contexts: - match: \) scope: punctuation.definition.parameters.end.elixir pop: true - - include: comma_and_skip_ws - - include: parameters + - include: parameters_or_if_closing_pop tuple_param: - match: \{ @@ -217,8 +373,7 @@ contexts: - match: \} scope: punctuation.section.sequence.end.elixir pop: true - - include: comma_and_skip_ws - - include: parameters + - include: parameters_or_if_closing_pop list_param: - match: \[ @@ -228,32 +383,31 @@ contexts: - match: \] scope: punctuation.section.brackets.end.elixir pop: true - - include: comma_and_skip_ws - - include: parameters - include: cons_operator + - include: parameters_or_if_closing_pop map_param: - match: \% scope: punctuation.section.mapping.begin.elixir push: - - match: _(?:{{module_name}}|{{identifier}})? - scope: variable.other.unused.elixir - set: map_param_body_pop + - meta_scope: meta.mapping.elixir + - match: (?=unquote\() + set: [map_param_body_pop, arguments_pop, unquote_pop] - include: alias_names + - match: ((?=_)(?){{identifier}})|({{identifier}}) + captures: + 1: variable.parameter.unused.elixir + 2: variable.parameter.elixir + set: map_param_body_pop - include: map_param_body_pop map_param_body_pop: - match: \{ scope: punctuation.section.mapping.begin.elixir set: - - meta_scope: meta.mapping.elixir - - match: \} - scope: punctuation.section.mapping.end.elixir - pop: true - - include: comma_and_skip_ws - - include: parameters - # No cons operator when parameter. - # - include: cons_operator + - include: map_closing_pop + - include: parameters_or_if_closing_pop + - include: if_closing_token_pop - include: if_non_space_or_eol_pop binary_string_param: @@ -263,7 +417,7 @@ contexts: push: - meta_scope: meta.string.binary.elixir - include: binary_string_body_pop - - include: parameters + - include: parameters_or_if_closing_pop ## Types @@ -274,162 +428,191 @@ contexts: spec_op_definition_pop: # E.g.: @spec integer &&& integer :: integer - - match: | - (?x) - ((?!::|\.){{operators}} | (?>not|in|and|or){{not_id_key_suffix}}) - scope: variable.other.type.elixir + - meta_scope: meta.type.elixir + - match: (?x)((?!::|\.(?!\.)){{operator}} | (?>and|in|or|not){{no_id_key_suffix}}) + scope: variable.other.spec.elixir set: spec_op_definition_next_pop + - include: if_types_end_pop - include: spec_op_definition_next_pop spec_op_definition_next_pop: + - meta_scope: meta.type.elixir - include: return_types_pop - - include: types + - match: (?=[,;]) + pop: true + - include: types_or_if_closing_pop - include: if_non_space_pop type_definition_pop: + - meta_scope: meta.type.elixir - match: (?=\() - set: [type_definition_next_pop, paren_param_pop] + set: [type_definition_next_pop, type_params_pop] - include: type_definition_next_pop + type_params_pop: + - match: \( + scope: punctuation.definition.parameters.begin.elixir + set: + - meta_scope: meta.type.elixir meta.function.parameters.elixir + - match: \) + scope: punctuation.definition.parameters.end.elixir + pop: true + - include: parameters_or_if_closing_pop + type_definition_next_pop: - include: comments - include: return_types_pop - include: if_non_space_pop types: - - include: comments - - include: unquote_call + - match: (?=unquote(?:_splicing)?\() + push: [type_arguments_or_pop, arguments_pop, unquote_pop] - include: block_or_keyword - - match: __MODULE__{{not_id_key_suffix}} - scope: variable.language.special-form.elixir + - include: special_form - match: | - (?x) - ((?> - # Basic types: - any|atom|float|map| - (?> - nonempty_char| - (?>(?:nonempty_)?maybe_|nonempty_)improper_|nonempty_ - | - )list| - none|pid|port|reference|tuple| - (?>(?:non_)?neg_|pos_)?integer| - # Built-in types: - arity|as_boolean|binary|bitstring|boolean|byte|charlist|char| - function|fun|identifier|iodata|iolist|keyword| - maybe_improper_list|mfa|module|no_return|node| - number|struct|term|timeout - )){{not_id_key_suffix}} - scope: support.type.elixir - push: type_arguments_or_pop - - match: (?>optional|required){{not_id_key_suffix}} - scope: keyword.other.elixir - push: type_arguments_or_pop - - match: '{{identifier}}' - scope: storage.type.custom.elixir + (?x)( + (?> # Ordered by likeliness of occurrence. + binary|term|atom|any|integer|boolean|map|list|module + | (?>(?:non_)?neg_|pos_)?integer|no_return|pid|iodata + | number|float|keyword|timeout|tuple|reference|charlist + | fun(?:ction)?|char|struct|arity|port|identifier|none\b|mfa + | node|byte|as_boolean|bitstring|iolist|maybe_improper_list + | (?> + nonempty_char + | (?>(?:nonempty_)?maybe_|nonempty_)improper_|nonempty_ + )?list + ) + ){{no_id_key_suffix}} + | (?>(optional)|(required)|(record))(?={{no_suffix_then_arguments}}) + | ({{identifier}}) + captures: + 1: support.type.elixir + 2: keyword.other.optional.elixir + 3: keyword.other.required.elixir + 4: keyword.other.record.elixir + 5: storage.type.custom.elixir push: type_arguments_or_pop - - include: paren_type - - include: tuple_type - include: list_type + - include: tuple_type + - include: module_name + - include: dot_type - include: map_type - include: binary_string_type - - include: dot_type - - include: module_name - - match: '[^\S\n]+' - comment: need to skip spaces here due to if_closing_tokens_no_eol_pop - - include: if_closing_tokens_no_eol_pop + - include: paren_type + - include: comments + + types_or_if_closing_pop: + - include: types + - include: if_closing_token_pop - include: core_syntax dot_type: - match: \.(?!\.) scope: punctuation.accessor.dot.elixir push: - - match: '{{identifier}}' + - include: unquote_call_pop + - match: '{{identifier}}{{no_id_key_suffix}}' scope: storage.type.remote.elixir set: type_arguments_or_pop - - include: if_non_space_pop + - include: if_non_space_or_eol_pop type_arguments_or_pop: - match: \( scope: punctuation.section.arguments.begin.elixir set: - - meta_scope: meta.function-call.arguments.elixir + - meta_scope: meta.type.elixir meta.function-call.arguments.elixir - include: arguments_closing_pop - include: arg_comma_and_skip_ws - include: named_type - - include: types - - include: union_operator - - include: empty_pop + - include: types_or_if_closing_pop + - include: if_empty_pop named_type: + - include: block_or_keyword - match: '{{identifier}}(?=\s*::(?!:))' scope: variable.other.named-type.elixir return_types_pop: - match: ::(?!:) - scope: keyword.operator.colon.elixir + scope: meta.type.elixir keyword.operator.colon.elixir set: [first_and_next_types_pop, if_non_space_pop] - first_and_next_types_pop: - - match: (?=when{{not_id_key_suffix}}) + if_types_end_pop: + # Avoid matching e.g. "def func" while typing the return types. + - match: (?=when{{no_id_key_suffix}}|@?{{identifier}}{{no_id_key_suffix}}\s{{has_arguments}}) comment: no when clause directly after "::" pop: true - - match: '' + - include: if_closing_token_pop + + first_and_next_types_pop: + - include: comments + - include: if_types_end_pop + - match: (?=\S) set: - - include: named_type - - match: (?=\|(?![:|>])|::(?!:)|when{{not_id_key_suffix}}) - set: next_types_pop + - meta_scope: meta.type.elixir # NB: we can't prevent matching more than one type expression. - - include: types - - include: if_eol_pop + # Just move to next_types_pop after the first newline, `|`, `::` or `when`. + - match: (?=$|\|(?![:|>])|::(?!:)|when{{no_id_key_suffix}}) + set: [next_types_pop, if_non_space_pop] + - match: (?=[,;]) + pop: true + - include: named_type + - include: types_or_if_closing_pop next_types_pop: - include: comments - - match: when{{not_id_key_suffix}} - scope: keyword.other.when.elixir - set: [when_types_clause_pop, if_non_space_pop] - - match: (\|)|(::(?!:)) + - match: when{{no_id_key_suffix}} + scope: keyword.operator.when.elixir + set: [when_types_clause_pop, invalid_comma_or_non_space_pop] + - match: (\|(?![:|>]))|(::(?!:)) captures: 1: keyword.operator.union.elixir 2: keyword.operator.colon.elixir push: [if_non_space_pop, union_types_eol_pop, if_non_space_pop] + - include: if_ws_closing_token_or_eol_pop - include: if_non_space_or_eol_pop union_types_eol_pop: + - meta_scope: meta.type.elixir + - include: comments + - match: | + (?x)(?= + [,;] | \|(?![:|>]) | ::(?!:) | when{{no_id_key_suffix}} + | @?{{identifier}}{{no_id_key_suffix}}\s{{has_arguments}} + ) + pop: true - include: named_type - - include: types + - include: types_or_if_closing_pop + - include: if_ws_closing_token_or_eol_pop - include: if_non_space_or_eol_pop when_types_clause_pop: - - include: comma_and_skip_ws + - meta_scope: meta.type.elixir - include: comments - include: atom_keyword - - match: (?<=[\w"'@]:) + - match: (?<=[^:]:) comment: skips to keyword value - push: [when_var_pop, if_non_space_pop] + push: [when_var_pop, invalid_comma_or_non_space_pop] + - include: comma_and_skip_ws + - include: if_ws_closing_token_or_eol_pop - include: if_non_space_or_eol_pop when_var_pop: - - match: var{{not_id_key_suffix}} + - match: var{{no_id_key_suffix}}(?!{{has_arguments}}) + comment: "`var` is a particular support type. Elixir recognizes it only after a `when`." scope: support.type.elixir - include: first_and_next_types_pop - union_operator: - - match: \| - scope: keyword.operator.union.elixir - paren_type: - match: \( - scope: punctuation.definition.parens.begin.elixir + scope: punctuation.section.group.begin.elixir push: - meta_scope: meta.parens.elixir - match: \) - scope: punctuation.definition.parens.end.elixir + scope: punctuation.section.group.end.elixir pop: true - - include: comma_and_skip_ws - include: named_type - - include: types - - include: union_operator + - include: types_or_if_closing_pop tuple_type: - match: \{ @@ -439,10 +622,8 @@ contexts: - match: \} scope: punctuation.section.sequence.end.elixir pop: true - - include: comma_and_skip_ws - include: named_type - - include: types - - include: union_operator + - include: types_or_if_closing_pop list_type: - match: \[ @@ -452,17 +633,18 @@ contexts: - match: \] scope: punctuation.section.brackets.end.elixir pop: true - - include: comma_and_skip_ws - include: named_type - - include: types - - include: union_operator + - include: types_or_if_closing_pop map_type: - match: \% scope: punctuation.section.mapping.begin.elixir push: + - meta_scope: meta.mapping.elixir + - match: (?=unquote\() + set: [map_type_body_pop, arguments_pop, unquote_pop] - include: alias_names - - match: _(?:{{module_name}}|{{identifier}})? + - match: _(?>{{module_name}}|{{identifier}})? scope: variable.type.unused.elixir invalid.illegal.struct.elixir set: map_type_body_pop - include: map_type_body_pop @@ -471,14 +653,10 @@ contexts: - match: \{ scope: punctuation.section.mapping.begin.elixir set: - - meta_scope: meta.mapping.elixir - - match: \} - scope: punctuation.section.mapping.end.elixir - pop: true - - include: comma_and_skip_ws + - include: map_closing_pop - include: named_type - - include: types - - include: union_operator + - include: types_or_if_closing_pop + - include: if_closing_token_pop - include: if_non_space_or_eol_pop binary_string_type: @@ -489,22 +667,33 @@ contexts: - meta_scope: meta.string.binary.elixir - include: binary_string_body_pop - include: named_type - - include: types - - include: union_operator + - include: types_or_if_closing_pop + + # Numeric numeric: - match: | - (?x) - ( - 0x\h[\h_]* | - 0b[01](?:_?[01])* | - 0o[0-7](?:_?[0-7])* | - \d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][-+]?\d(?:_?\d)*)? - )(\w*) + (?x)(?> + (0 (x) \h (?:_?\h)*) + | (0 (b) [01] (?:_?[01])*) + | (0 (o) [0-7] (?:_?[0-7])*) + | (\d(?:_?\d)* (\.) \d(?:_?\d)* (?:[eE] [-+]? \d(?:_?\d)*)?) + | (\d(?:_?\d)*) + )([_\d]*) comment: (?:_?\d)* because double or trailing '_' are invalid captures: - 1: constant.numeric.elixir - 2: invalid.illegal.numeric.elixir + 1: constant.numeric.hex.elixir + 2: punctuation.separator.numeric.elixir + 3: constant.numeric.binary.elixir + 4: punctuation.separator.numeric.elixir + 5: constant.numeric.octal.elixir + 6: punctuation.separator.numeric.elixir + 7: constant.numeric.float.elixir + 8: punctuation.separator.decimal.elixir + 9: constant.numeric.integer.elixir + 10: invalid.illegal.numeric.elixir + + ## Strings binary_string: - include: stray_binary_end @@ -513,7 +702,7 @@ contexts: push: - meta_scope: meta.string.binary.elixir - include: binary_string_body_pop - - include: core_syntax + - include: core_syntax_or_if_closing_pop stray_binary_end: - match: '>>(?!>)' @@ -523,38 +712,51 @@ contexts: - match: '>>(?!>)' scope: string.other.binary.elixir punctuation.definition.string.end.elixir pop: true - - include: comma_and_skip_ws - match: ::(?!:) scope: keyword.operator.colon.elixir push: - meta_scope: meta.type.binary.elixir - - include: unquote_call - - match: '{{identifier}}|[-*\d]+' - scope: storage.type.binary.elixir - - match: (?=\() - push: arguments_pop + - include: bitstring_type - include: if_non_space_pop + bitstring_type: + - include: atom_symbol + - include: block_or_keyword + - include: unquote_call + - match: -(?![->])|\*|(?>\d(?:_?\d)*) + scope: storage.type.binary.elixir + - match: '{{identifier}}' + scope: storage.type.binary.elixir + push: arguments_or_pop + - include: paren_binary + + paren_binary: + - match: \( + scope: punctuation.section.group.begin.elixir + push: + - meta_scope: meta.parens.elixir + - match: \) + scope: punctuation.section.group.end.elixir + pop: true + - include: bitstring_type + - include: core_syntax_or_if_closing_pop + escaped_char: - - match: \\x\h{2} - scope: constant.character.escape.hex.elixir - # Avoid possibly matching closing string delimiter: [^'"/)\]}>] - - match: \\x(?:\h|([^'"/)\]}>])){1,2} + # Avoid possibly matching closing string delimiter: [^'"/\\)\]}>#] + - match: (?x) \\x (?> \h{2} | (?>\h|([^'"/\\)\]}>#])){1,2} | ) scope: constant.character.escape.hex.elixir captures: 1: invalid.illegal.escape.hex.elixir - 2: invalid.illegal.escape.hex.elixir - - match: \\u(?:\h{4}|{\h{1,6}}) + - match: (?x) \\u (?> (?>\h{4}|{\h{1,6}}) | ({}|{?[^'"/\\)\]}>#]{1,6}}?)) scope: constant.character.escape.unicode.elixir - - match: \\u(?:{}|{?[^'"/)\]}>]{1,6}}?) - scope: invalid.illegal.escape.unicode.elixir - - match: \\.|\\\n + captures: + 1: invalid.illegal.escape.unicode.elixir + - match: \\. scope: constant.character.escape.char.elixir interpolated_elixir: - match: (?=#{) push: - # TODO: could use 1, but need to adjust scopes in rules using escaped_or_interpolated. - clear_scopes: true - match: '#{' scope: punctuation.section.interpolation.begin.elixir @@ -568,16 +770,16 @@ contexts: - include: core_syntax escaped_or_interpolated: + - include: line_continuation - include: escaped_char - include: interpolated_elixir - regex_elixir: - - include: scope:source.regexp.elixir + pcre_erlang: + - include: scope:source.pcree - regex_or_interpolated: + elixir_in_regex: + - include: line_continuation - include: interpolated_elixir - - include: regex_elixir - simple_string: - match: \' @@ -609,9 +811,9 @@ contexts: - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - include: heredoc_string_closing_double_pop - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=^(?>\\.|[^"])*?""") pop: true @@ -623,9 +825,9 @@ contexts: - meta_scope: meta.string.elixir string.quoted.triple.single.elixir - include: heredoc_string_closing_single_pop - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=^(?>\\.|[^'])*?''') pop: true @@ -639,7 +841,7 @@ contexts: - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - include: heredoc_string_closing_double_pop - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - match: (?=^(?>\\.|[^"])*?""") pop: true @@ -653,7 +855,7 @@ contexts: - meta_scope: meta.string.elixir string.quoted.triple.single.elixir - include: heredoc_string_closing_single_pop - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - match: (?=^(?>\\.|[^'])*?''') pop: true @@ -688,8 +890,6 @@ contexts: push: - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - include: heredoc_string_closing_double_pop - - match: \\" - scope: constant.character.escape.char.elixir - match: (''')(.*)\n comment: Triple-quoted heredocs @@ -699,8 +899,6 @@ contexts: push: - meta_scope: meta.string.elixir string.quoted.triple.single.elixir - include: heredoc_string_closing_single_pop - - match: \\' - scope: constant.character.escape.char.elixir heredoc_string_closing_double_pop: - match: ^\s*((?>\\.|[^"])*?)\s*(""") @@ -743,7 +941,7 @@ contexts: - match: \s+ push: - clear_scopes: 1 - - include: empty_pop + - include: if_empty_pop - include: escaped_or_interpolated - match: (\2)(a) captures: @@ -751,42 +949,65 @@ contexts: 2: string.quoted.modifiers.elixir storage.type.string.elixir pop: true - - match: ~L + - match: ~L(?=["'])(?!''') comment: LiveView scope: meta.string.elixir storage.type.string.elixir push: - match: (""")(.*)\n captures: - 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 1: punctuation.definition.string.begin.elixir 2: invalid.illegal.opening-heredoc.elixir set: - meta_scope: meta.string.elixir + # NB: works but doesn't put meta.string.elixir on """ + # - match: (?=^(?>\\.|[^"])*?""") + # set: [string_modifiers_and_pop, heredoc_string_closing_double_pop] + - match: (?<=""") + set: string_modifiers_and_pop - match: (?=^(?>\\.|[^"])*?""") - set: - - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - - include: heredoc_string_closing_double_pop + push: heredoc_string_closing_double_pop - match: '' push: scope:text.html.eex with_prototype: - - match: \\" - scope: constant.character.escape.char.elixir - match: (?=^(?>\\.|[^"])*?""") pop: true + - match: \" + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - include: string_closing_dquote + - match: '' + push: scope:text.html.eex + with_prototype: + - match: \\" + - match: (?=") + pop: true + - match: \' + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - include: string_closing_squote + - match: '' + push: scope:text.html.eex + with_prototype: + - match: \\' + - match: (?=') + pop: true - - match: ~y + - match: ~y(?=""") comment: YAML with interpolation scope: meta.string.elixir storage.type.string.elixir push: - match: (""")(.*)\n captures: - 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 1: punctuation.definition.string.begin.elixir 2: invalid.illegal.opening-heredoc.elixir set: - meta_scope: meta.string.elixir + - match: (?<=""") + set: string_modifiers_and_pop - match: (?=^(?>\\.|[^"])*?""") - set: - - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - - include: heredoc_string_closing_double_pop + push: heredoc_string_closing_double_pop - match: '' push: scope:source.yaml with_prototype: @@ -794,70 +1015,110 @@ contexts: - match: (?=^(?>\\.|[^"])*?""") pop: true - - match: ~Y + - match: ~Y(?=""") comment: YAML raw scope: meta.string.elixir storage.type.string.elixir push: - match: (""")(.*)\n captures: - 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 1: punctuation.definition.string.begin.elixir 2: invalid.illegal.opening-heredoc.elixir set: - meta_scope: meta.string.elixir + - match: (?<=""") + set: string_modifiers_and_pop - match: (?=^(?>\\.|[^"])*?""") - set: - - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - - include: heredoc_string_closing_double_pop + push: heredoc_string_closing_double_pop - match: '' push: scope:source.yaml with_prototype: - - match: \\" - scope: constant.character.escape.char.elixir - match: (?=^(?>\\.|[^"])*?""") pop: true - - match: ~j + - match: ~j(?=["'])(?!''') comment: JSON with interpolation scope: meta.string.elixir storage.type.string.elixir push: - match: (""")(.*)\n captures: - 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 1: punctuation.definition.string.begin.elixir 2: invalid.illegal.opening-heredoc.elixir set: - meta_scope: meta.string.elixir + - match: (?<=""") + set: string_modifiers_and_pop - match: (?=^(?>\\.|[^"])*?""") - set: - - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - - include: heredoc_string_closing_double_pop + push: heredoc_string_closing_double_pop - match: '' push: scope:source.json with_prototype: - - include: interpolated_elixir + - include: escaped_or_interpolated - match: (?=^(?>\\.|[^"])*?""") pop: true + - match: \" + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - include: string_closing_dquote + - match: '' + push: scope:source.json + with_prototype: + - include: escaped_or_interpolated + - match: (?=") + pop: true + - match: \' + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - include: string_closing_squote + - match: '' + push: scope:source.json + with_prototype: + - include: escaped_or_interpolated + - match: (?=') + pop: true - - match: ~J + - match: ~J(?=["'])(?!''') comment: JSON raw scope: meta.string.elixir storage.type.string.elixir push: - match: (""")(.*)\n captures: - 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 1: punctuation.definition.string.begin.elixir 2: invalid.illegal.opening-heredoc.elixir set: - meta_scope: meta.string.elixir + - match: (?<=""") + set: string_modifiers_and_pop - match: (?=^(?>\\.|[^"])*?""") - set: - - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - - include: heredoc_string_closing_double_pop + push: heredoc_string_closing_double_pop - match: '' push: scope:source.json with_prototype: - - match: \\" - scope: constant.character.escape.char.elixir - match: (?=^(?>\\.|[^"])*?""") pop: true + - match: \" + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - include: string_closing_dquote + - match: '' + push: scope:source.json + with_prototype: + - match: \\" + - match: (?=") + pop: true + - match: \' + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - include: string_closing_squote + - match: '' + push: scope:source.json + with_prototype: + - match: \\' + - match: (?=') + pop: true - match: ~r comment: regex sigil string with interpolation @@ -872,48 +1133,44 @@ contexts: scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.interpolated.elixir - - match: \" - set: string_modifiers_and_pop + - include: string_closing_dquote - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=") pop: true - match: \' scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.interpolated.elixir - - match: \' - set: string_modifiers_and_pop + - include: string_closing_squote - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=') pop: true - match: / scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.interpolated.elixir - - match: / - set: string_modifiers_and_pop + - include: string_closing_slash - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=/) pop: true - match: \| scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.interpolated.elixir - - match: \| - set: string_modifiers_and_pop + - include: string_closing_pipe - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=\|) pop: true - match: \{ @@ -922,9 +1179,9 @@ contexts: - meta_scope: meta.string.elixir string.interpolated.elixir - include: string_closing_curly - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=}) pop: true - match: \[ @@ -933,9 +1190,9 @@ contexts: - meta_scope: meta.string.elixir string.interpolated.elixir - include: string_closing_square - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=]) pop: true - match: \< @@ -944,9 +1201,9 @@ contexts: - meta_scope: meta.string.elixir string.interpolated.elixir - include: string_closing_angle - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - match: (?=>) pop: true - match: \( @@ -955,9 +1212,9 @@ contexts: - meta_scope: meta.string.elixir string.interpolated.elixir - include: string_closing_round - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: - - include: interpolated_elixir + - include: elixir_in_regex - include: if_closing_paren_pop - match: ~R @@ -976,7 +1233,7 @@ contexts: # set: string_modifiers_and_pop # - match: ([/|"']) # scope: punctuation.definition.string.begin.elixir - # push: regex_elixir + # push: pcre_erlang # with_prototype: # - match: \1 # scope: punctuation.definition.string.end.elixir @@ -985,44 +1242,48 @@ contexts: scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - - match: \" - set: string_modifiers_and_pop + - include: string_closing_dquote - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\" + scope: constant.character.escape.pcree - match: (?=") pop: true - match: \' scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - - match: \' - set: string_modifiers_and_pop + - include: string_closing_squote - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\' + scope: constant.character.escape.pcree - match: (?=') pop: true - match: / scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - - match: / - set: string_modifiers_and_pop + - include: string_closing_slash - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\/ + scope: constant.character.escape.pcree - match: (?=/) pop: true - match: \| scope: punctuation.definition.string.begin.elixir set: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - - match: \| - set: string_modifiers_and_pop + - include: string_closing_pipe - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\\| + scope: constant.character.escape.pcree - match: (?=\|) pop: true - match: \{ @@ -1031,8 +1292,10 @@ contexts: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - include: string_closing_curly - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\} + scope: constant.character.escape.pcree - match: (?=}) pop: true - match: \[ @@ -1041,8 +1304,10 @@ contexts: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - include: string_closing_square - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\] + scope: constant.character.escape.pcree - match: (?=]) pop: true - match: \< @@ -1051,8 +1316,10 @@ contexts: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - include: string_closing_angle - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\> + scope: constant.character.escape.pcree - match: (?=>) pop: true - match: \( @@ -1061,8 +1328,10 @@ contexts: - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir - include: string_closing_round - match: '' - push: regex_elixir + push: pcre_erlang with_prototype: + - match: \\\) + scope: constant.character.escape.pcree - include: if_closing_paren_pop - match: ~[a-z] @@ -1132,11 +1401,10 @@ contexts: 1: string.quoted.other.literal.upper.elixir punctuation.definition.string.begin.elixir push: - meta_content_scope: string.quoted.other.literal.upper.elixir + - match: \\. - match: \1 scope: string.quoted.other.literal.upper.elixir punctuation.definition.string.end.elixir pop: true - - match: \\[/|"'] - scope: constant.character.escape.char.elixir - match: '' set: string_modifiers_and_pop - match: \{ @@ -1160,6 +1428,26 @@ contexts: - meta_scope: meta.string.elixir string.quoted.other.literal.upper.elixir - include: string_closing_round + string_closing_dquote: + - match: \" + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_closing_squote: + - match: \' + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_closing_pipe: + - match: \| + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_closing_slash: + - match: \/ + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + string_closing_curly: - match: \\\} scope: constant.character.escape.char.elixir @@ -1196,32 +1484,34 @@ contexts: string_modifiers_and_pop: - include: string_modifiers - - include: empty_pop + - include: if_empty_pop + + ## Declarations def_blocks: + - match: | + (?x) + (?>(def(?>macro|guard|n)?p) | (def(?>macro|delegate|guard|n)?)) + {{no_suffix_then_arguments}} + captures: + 1: keyword.declaration.function.private.elixir + 2: keyword.declaration.function.public.elixir + push: function_header_pop - match: defmodule{{no_suffix_then_arguments}} scope: keyword.declaration.module.elixir push: - meta_scope: meta.namespace.module.elixir - include: defmodule_body_pop - - match: defprotocol{{no_suffix_then_arguments}} - scope: keyword.declaration.protocol.elixir - push: - - meta_scope: meta.namespace.protocol.elixir - - include: defmodule_body_pop - match: defimpl{{no_suffix_then_arguments}} scope: keyword.declaration.implementation.elixir push: - meta_scope: meta.namespace.implementation.elixir - include: arguments_paren_or_ws_pop - - match: | - (?x) - (?>(def(?>delegate|macro|guard|)p) - | (def(?>delegate|macro|guard|)) {{no_suffix_then_arguments}}) - captures: - 1: keyword.declaration.function.private.elixir - 2: keyword.declaration.function.public.elixir - push: function_header_pop + - match: defprotocol{{no_suffix_then_arguments}} + scope: keyword.declaration.protocol.elixir + push: + - meta_scope: meta.namespace.protocol.elixir + - include: defmodule_body_pop defmodule_body_pop: - match: \( @@ -1238,80 +1528,183 @@ contexts: - include: defmodule_arguments defmodule_arguments: + - match: (?={{atom_symbol}}:(?!:))|(?<=[^:]:) + push: arguments_ws_rest_pop - include: block_or_keyword - - match: (?={{module_name}}|__MODULE__{{not_id_key_suffix}}|:{{atom_symbol}}) + - match: | + (?x)(?= + (?:(?<=%>)\.)? (?>{{module_name}}{{no_key_suffix}} | __MODULE__{{no_id_key_suffix}}) + | :(?>{{atom_symbol}}|['"]) + ) push: - meta_scope: meta.namespace.elixir - - match: (:)({{atom_symbol}})(?!\s*\.) - scope: constant.other.symbol.elixir - captures: - 1: punctuation.definition.constant.begin.elixir - 2: entity.name.namespace.elixir - - match: '{{module_name}}(?!\s*\.)' + - match: '{{module_name}}{{no_key_suffix}}(?!\s*\.(?!\.))' scope: entity.name.namespace.elixir + - include: atom_namespace_entity - include: special_form - include: module_name - include: atom_symbol - - include: module_accessor_or_pop + - include: unquote_call + - include: dot_operator + - include: if_non_space_or_eol_pop - include: arg_comma_and_skip_ws - - include: core_syntax + - include: core_syntax_or_if_closing_pop + + defrecord: + - match: (?:(Record)\s*(\.)\s*)?(defrecordp?){{no_suffix_then_arguments}} + captures: + 1: constant.other.module.elixir + 2: punctuation.accessor.dot.elixir + 3: keyword.declaration.elixir + push: defrecord_pop + + defrecord_pop: + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_closing_pop + - include: defrecord_arguments + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_ws_closing_pop + - include: defrecord_arguments + + defrecord_arguments: + - include: atom_function_entity + - match: (?=,) + push: arguments_ws_rest_pop + - include: arg_comma_and_skip_ws + - include: core_syntax_or_if_closing_pop + + atom_function_entity: + - match: (:)({{atom_symbol}})(?!\s*\.(?!\.)) + scope: constant.other.symbol.elixir + captures: + 1: punctuation.definition.constant.begin.elixir + 2: entity.name.function.elixir + + atom_namespace_entity: + - match: (:)({{atom_symbol}})(?!\s*\.(?!\.)) + scope: constant.other.symbol.elixir + captures: + 1: punctuation.definition.constant.begin.elixir + 2: entity.name.namespace.elixir + + block_or_keyword: + - include: atom_keyword + - include: fn_block + - include: do_block + - include: elixir_keywords do_block_pop: - - match: do{{not_id_key_suffix}} - scope: punctuation.section.block.begin.elixir keyword.context.block.elixir + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir set: - meta_scope: meta.block.elixir - - include: block_end_pop - - include: core_syntax + - match: else{{no_id_key_suffix}} + comment: the else-block contains arrow clauses only in error handling + scope: keyword.control.conditional.else.elixir + - include: error_handling_clauses + - include: core_syntax_or_block_end_pop + + func_do_block_pop: + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir + set: + - meta_scope: meta.block.elixir + - include: error_handling_clauses + - include: core_syntax_or_block_end_pop + + error_handling_clauses: + - match: else{{no_id_key_suffix}} + scope: keyword.control.exception.else.elixir + push: arrow_clauses_body_pop + - match: after{{no_id_key_suffix}} + scope: keyword.control.exception.after.elixir + push: core_syntax_or_if_closing_pop + - match: rescue{{no_id_key_suffix}} + scope: keyword.control.exception.rescue.elixir + push: arrow_clauses_body_pop + - match: catch{{no_id_key_suffix}} + scope: keyword.control.exception.catch.elixir + push: arrow_clauses_body_pop do_block: # NB: Commented out due to EEx templates: <%= if x do %>...<% end %> - # - match: end{{not_id_key_suffix}} + # - match: end{{no_id_key_suffix}} # scope: invalid.illegal.block-end.elixir - - match: (?=do{{not_id_key_suffix}}) + - match: (?=do{{no_id_key_suffix}}) push: do_block_pop block_end_pop: - - match: end{{not_id_key_suffix}} - scope: punctuation.section.block.end.elixir keyword.context.block.elixir + - match: end{{no_id_key_suffix}} + scope: punctuation.section.block.end.elixir keyword.context.block.end.elixir pop: true - module_name: - - match: '{{module_name}}{{not_id_key_suffix}}' - scope: constant.other.module.elixir + last_arg_block_end_pop: + - match: end{{no_id_key_suffix}} + scope: punctuation.section.block.end.elixir keyword.context.block.end.elixir + set: + - match: \s|\n| + scope: punctuation.section.arguments.end.elixir + pop: true - module_names: - - match: (?={{module_name}}) - push: - - meta_scope: meta.path.modules.elixir - - match: '{{module_name}}' - scope: constant.other.module.elixir - - include: module_accessor_or_pop + core_syntax_or_block_end_pop: + - include: block_end_pop + - include: core_syntax - module_accessor_or_pop: - - match: \.(?!\.) - scope: punctuation.accessor.elixir - - include: if_non_space_or_eol_pop + core_syntax_or_if_closing_pop: + - include: if_closing_token_pop + - include: core_syntax + + ## Identifiers + + module_name: + - match: '{{module_name}}{{no_key_suffix}}' + scope: constant.other.module.elixir + - match: (?x)(:(["'])) ((?>\\\\|\\\2|(?!\2).)*?) (\2) (?=\s*\.(?!\.)) + captures: + 1: punctuation.definition.constant.begin.elixir + 3: constant.other.module.elixir + 4: punctuation.definition.constant.end.elixir + - match: (:)({{atom_symbol}})(?=\s*\.(?!\.)) + captures: + 1: punctuation.definition.constant.begin.elixir + 2: constant.other.module.elixir + module_name_pop: + - match: '{{module_name}}{{no_key_suffix}}' + scope: constant.other.module.elixir + pop: true module_function_call_pop: - - match: ({{module_name}})\s*(\.(?!\.))\s*(?={{identifier}}|{{operators}}) + - match: ({{module_name}})\s*(\.(?!\.)) comment: always a function call after a module captures: 1: constant.other.module.elixir 2: punctuation.accessor.dot.elixir - set: - # NB: match separately so that <%%= Routes.<%= xyz %> %> is recognised correctly. - - match: '{{identifier}}|{{operators}}' - scope: variable.function.elixir - set: arguments_or_pop + set: member_call_pop + + member_call_pop: + # - include: sql_or_fragment + - include: quoted_remote_call_pop + - include: quoted_member_pop + - include: unquote_call_pop + - match: '{{member}}' + scope: variable.function.elixir + set: arguments_or_pop + - include: tuple_call_pop + - include: if_non_space_or_eol_pop - identifier_operator_call_pop: - - match: ({{identifier}}|{{operators}})(?=\s*\.\s*\(|{{has_arguments}}) + id_or_operator_call_pop: + - match: ({{member}})(?=\s*\.\s*\(|{{has_arguments}}) scope: variable.function.elixir set: arguments_paren_or_ws_pop quoted_remote_call_pop: - - match: \" + - match: \"(?=(?>\\[\\"]|.)*?"{{has_arguments}}) scope: punctuation.definition.constant.begin.elixir set: - meta_scope: meta.function-call.elixir @@ -1322,7 +1715,7 @@ contexts: scope: variable.function.elixir captures: 1: constant.character.escape.char.elixir - - match: \' + - match: \'(?=(?>\\[\\']|.)*?'{{has_arguments}}) scope: punctuation.definition.constant.begin.elixir set: - meta_scope: meta.function-call.elixir @@ -1334,41 +1727,88 @@ contexts: captures: 1: constant.character.escape.char.elixir - modules_or_identifiers_or_calls: + quoted_member_pop: + - match: \" + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: meta.member.elixir + - match: \" + scope: punctuation.definition.constant.end.elixir + pop: true + - match: (\\[\\"])|[^"] + scope: variable.other.member.elixir + captures: + 1: constant.character.escape.char.elixir + - match: \' + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: meta.member.elixir + - match: \' + scope: punctuation.definition.constant.end.elixir + pop: true + - match: (\\[\\'])|[^'] + scope: variable.other.member.elixir + captures: + 1: constant.character.escape.char.elixir + + modules_or_ids_or_calls: + - match: (?x)(:(["'])) ((?>\\\\|\\\2|(?!\2).)*?) (\2) (?=\s*\.(?!\.)) + captures: + 1: punctuation.definition.constant.begin.elixir + 3: constant.other.module.elixir + 4: punctuation.definition.constant.end.elixir + push: member_call_pop + - match: (:)({{atom_symbol}})\s*(\.(?!\.)) + captures: + 1: punctuation.definition.constant.begin.elixir + 2: constant.other.module.elixir + 3: punctuation.accessor.dot.elixir + push: member_call_pop - match: (?={{module_name}}|{{identifier}}) push: - - meta_scope: meta.path.modules-identifiers.elixir - include: module_function_call_pop - - include: identifier_operator_call_pop - - match: '{{module_name}}' - scope: constant.other.module.elixir - pop: true - - match: _{{identifier}}? - scope: variable.other.unused.elixir - pop: true - - match: '{{identifier}}' - scope: variable.other.elixir + - include: id_or_operator_call_pop + - include: module_name_pop + - include: identifier_pop + + identifier_pop: + - match: ((?=_)(?){{identifier}})|({{identifier}}) + captures: + 1: variable.other.unused.elixir + 2: variable.other.elixir + pop: true + + id_member_pop: + - match: '{{member}}' + scope: variable.other.member.elixir + pop: true + + tuple_call_pop: + - match: \{ + scope: punctuation.section.braces.begin.elixir + set: + - match: \} + scope: punctuation.section.braces.end.elixir pop: true + - include: core_syntax_or_if_closing_pop dot_accessor: - match: \.(?!\.) scope: punctuation.accessor.dot.elixir push: - - match: (?>{{identifier}}|{{operators}}){{not_id_key_suffix}}(?=\s*do{{not_id_key_suffix}}) - comment: avoids matching 'xyz' as a function call, e.g. "func 1, 2, map.xyz do ... end" - scope: variable.other.member.elixir - pop: true + - include: atom_keyword - include: module_function_call_pop - - include: identifier_operator_call_pop - - include: quoted_remote_call_pop + - include: unquote_call_pop + - include: id_or_operator_call_pop + - include: id_member_pop + - include: module_name_pop - include: arguments_pop - - match: '{{identifier}}|{{operators}}' - scope: variable.other.member.elixir - pop: true - - match: '{{module_name}}' - scope: constant.other.module.elixir - pop: true - - include: if_non_space_pop + - include: quoted_remote_call_pop + - include: quoted_member_pop + - include: tuple_call_pop + - include: if_non_space_or_eol_pop + + ## Module attributes module_attribute: - match: '@' @@ -1376,109 +1816,132 @@ contexts: push: module_attribute_pop module_attribute_pop: - - match: (?!{{identifier}}(?!{{atom_id_suffix}}:)) + - match: (?!{{identifier}}(?!{{atom_id_suffix}}:(?!:))) set: - match: '[A-Z]' scope: invalid.illegal.attribute.elixir - - include: empty_pop + - include: if_empty_pop # Special attributes: - match: (?>(?>module|type|short)?doc){{no_suffix_then_arguments}} scope: support.attr.doc.elixir set: - - meta_content_scope: comment.block.documentation.elixir - - include: markdown_comment - - include: if_non_space_or_eol_pop + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.doc.elixir + - include: arguments_closing_pop + - match: '' + push: + - include: if_closing_token_pop + - include: arg_comma_and_skip_ws + - include: markdown_comment + - include: core_syntax + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.doc.elixir + - include: arguments_ws_closing_pop + - include: arg_comma_and_skip_ws + - include: markdown_comment + - include: core_syntax - - match: (?>derive|deprecated|impl|file|fallback_to_any|behaviour|vsn|(?>before_|after_)?compile|dialyzer|external_resource|on_(?>definition|load)){{no_suffix_then_arguments}} - scope: support.attr.elixir - pop: true - - match: (?>spec|(?:macro)?callback)(?={{no_suffix_then_arguments}}|\s*{{operators}}) + - match: (?>spec|(?:macro)?callback)(?={{no_suffix_then_arguments}}|\s*{{operator}}) scope: keyword.declaration.type.elixir set: + - meta_scope: meta.type.elixir - match: \( scope: punctuation.section.arguments.begin.elixir set: - include: arguments_closing_pop + - match: '' + push: + - match: (?=not{{no_id_key_suffix}}) + set: spec_header_pop + - include: block_or_keyword + - include: spec_header_pop + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - match: (?=not{{no_id_key_suffix}}) + set: spec_header_pop - include: block_or_keyword - - match: unquote(?=\s*\() - scope: keyword.other.elixir - push: [spec_definition_pop, arguments_pop] - - match: (?={{identifier}}|{{operators}}) - push: spec_header_pop - - include: core_syntax - - include: block_or_keyword - - match: unquote(?=\s*\() - scope: keyword.other.elixir - set: [spec_definition_pop, arguments_pop] - - include: spec_header_pop - - include: if_non_space_pop + - include: spec_header_pop + - include: if_non_space_pop - match: (?>typep?|opaque){{no_suffix_then_arguments}} scope: keyword.declaration.type.elixir set: - # TODO: - # - meta_content_scope: meta.type.elixir + - meta_scope: meta.type.elixir - match: \( scope: punctuation.section.arguments.begin.elixir set: - - include: arguments_closing_pop + - meta_scope: meta.type.elixir meta.function-call.arguments.elixir - include: block_or_keyword - - match: unquote(?=\s*\() - scope: keyword.other.elixir - push: [type_definition_pop, arguments_pop] + - match: (?=unquote\() + push: [type_definition_pop, arguments_pop, unquote_pop] - match: '{{identifier}}' scope: entity.name.type.elixir push: type_definition_pop - - include: core_syntax - - include: block_or_keyword - - match: unquote(?=\s*\() - scope: keyword.other.elixir - set: [type_definition_pop, arguments_pop] - - match: '{{identifier}}' - scope: entity.name.type.elixir - set: type_definition_pop - - include: if_non_space_pop + - include: arguments_rest_pop + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.type.elixir meta.function-call.arguments.elixir + - include: block_or_keyword + - match: (?=unquote\() + set: [type_definition_pop, arguments_pop, unquote_pop] + - match: '{{identifier}}' + scope: entity.name.type.elixir + set: type_definition_pop + - include: if_non_space_pop + + - match: | + (?x)(?> + impl | deprecated | (?>before_|after_)?compile | behaviour | enforce_keys + | fallback_to_any | optional_callbacks | file | dialyzer | derive + | external_resource | vsn | on_(?>definition|load) + ){{no_suffix_then_arguments}} + scope: support.attr.elixir + set: arguments_paren_or_ws_pop - include: block_or_keyword # Regular attributes: - match: '{{identifier}}{{has_arguments}}' comment: definition - # FIXME: Sublime doesn't list it as a "Goto"-symbol for some reason. scope: entity.name.constant.elixir - pop: true + set: arguments_paren_or_ws_pop + - match: '{{identifier}}' comment: reference - # FIXME: "Goto Definition" doesn't work no matter which scope is used. scope: variable.other.constant.elixir pop: true - block_or_keyword: - - include: atom_keyword - - include: fn_block - - include: do_block - - include: elixir_keywords + spec_after_unquote_pop: + - match: (?=(?!::){{operator}}) + set: spec_op_definition_pop + - match: '' + set: spec_definition_pop spec_header_pop: - # FIXME: try to handle "@spec (x | y) + z"? - - match: | - (?x) - (?= - ({{operators}}|not{{not_id_key_suffix}}) - | ({{identifier}}) \s* ((?!::){{operators}} | (?>in|and|or){{not_id_key_suffix}}) - | {{module_name}} #\s* \. {{identifier}} - ) - set: spec_op_definition_pop - - match: '{{identifier}}' - scope: variable.other.type.elixir + - match: (?=unquote\() + set: [spec_after_unquote_pop, if_non_space_or_eol_pop, arguments_pop, unquote_pop] + - match: '{{identifier}}(?!\s*(?!::){{operator}})' + scope: variable.other.spec.elixir set: spec_definition_pop + - match: (?=\S) + set: spec_op_definition_pop + + ## Documentation in Markdown markdown_comment: - - match: (""")(.*)\n + # NB: no need to support `~s"""` which is basically `"""`. + - match: (""")(.*)(\n) captures: 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir 2: invalid.illegal.opening-heredoc.elixir + 3: string.quoted.triple.double.elixir push: - meta_scope: meta.string.elixir - match: (?=^\s*""") @@ -1486,11 +1949,12 @@ contexts: - meta_scope: meta.string.elixir string.quoted.triple.double.elixir - include: heredoc_string_closing_double_pop - include: escaped_or_interpolated - - include: elixir_in_markdown + # - include: elixir_in_markdown - match: '' embed: scope:text.html.markdown - embed_scope: text.html.eex source.elixir.embedded.html - escape: (?=^\s*"""|(?<=\s{4})(?:iex.*?>|```\s*elixir\b|(?>cond|receive|try)\s+do\b|def(?>module|impl|macro|guard|delegate|protocol|p|){{not_id_key_suffix}})|\\|#{) + embed_scope: source.markdown.embedded.elixir + # escape: (?=^\s*"""|\\|#{|{{is_markdown_elixir}}) + escape: (?=^\s*"""|\\|#{) - match: \" scope: string.quoted.double.elixir punctuation.definition.string.begin.elixir @@ -1502,107 +1966,85 @@ contexts: - include: escaped_or_interpolated - match: '' embed: scope:text.html.markdown - embed_scope: text.html.eex source.elixir.embedded.html + embed_scope: source.markdown.embedded.elixir escape: (?="|\\|#{) - - match: ~S(""")(.*)\n - scope: meta.string.elixir storage.type.string.elixir + - match: (~S)(""")(.*)(\n) captures: - 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir - 2: invalid.illegal.opening-heredoc.elixir + 1: storage.type.string.elixir + 2: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 3: invalid.illegal.opening-heredoc.elixir + 4: string.quoted.triple.double.elixir push: - meta_scope: meta.string.elixir + - match: (?<=""") + set: string_modifiers_and_pop - match: (?=^\s*""") - set: - - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + push: + - meta_scope: string.quoted.triple.double.elixir - include: heredoc_string_closing_double_pop - - include: elixir_in_markdown + # - include: elixir_in_markdown - match: '' embed: scope:text.html.markdown - embed_scope: text.html.eex source.elixir.embedded.html - escape: (?=^\s*"""|(?<=\s{4})(?:iex.*?>|```\s*elixir\b|(?>cond|receive|try)\s+do\b|def(?>module|impl|macro|guard|delegate|protocol|p|){{not_id_key_suffix}})) + embed_scope: source.markdown.embedded.elixir + # escape: (?=^\s*"""|{{is_markdown_elixir}}) + escape: (?=^\s*""") - - match: ~S(''')(.*)\n - scope: meta.string.elixir storage.type.string.elixir + - match: (~S)(''')(.*)(\n) captures: - 1: string.quoted.triple.single.elixir punctuation.definition.string.begin.elixir - 2: invalid.illegal.opening-heredoc.elixir + 1: storage.type.string.elixir + 2: string.quoted.triple.single.elixir punctuation.definition.string.begin.elixir + 3: invalid.illegal.opening-heredoc.elixir + 4: string.quoted.triple.single.elixir push: - meta_scope: meta.string.elixir + - match: (?<=''') + set: string_modifiers_and_pop - match: (?=^\s*''') - set: - - meta_scope: meta.string.elixir string.quoted.triple.single.elixir + push: + - meta_scope: string.quoted.triple.single.elixir - include: heredoc_string_closing_single_pop - - include: elixir_in_markdown + # - include: elixir_in_markdown - match: '' embed: scope:text.html.markdown - embed_scope: text.html.eex source.elixir.embedded.html - escape: (?=^\s*'''|(?<=\s{4})(?:iex.*?>|```\s*elixir\b|(?>cond|receive|try)\s+do\b|def(?>module|impl|macro|guard|delegate|protocol|p|){{not_id_key_suffix}})) + embed_scope: source.markdown.embedded.elixir + # escape: (?=^\s*'''|{{is_markdown_elixir}}) + escape: (?=^\s*''') elixir_in_markdown: - - match: (?<=\s{4})(?=(?>(?>cond|receive|try)\s+do|def(?>module|impl|macro|guard|delegate|protocol|p|)){{not_id_key_suffix}}) - push: - - clear_scopes: true - - meta_scope: meta.interpolation.elixir markup.raw.block.markdown markup.raw.block.elixir - - match: \n|$ - scope: punctuation.section.arguments.end.elixir - pop: true - - include: core_syntax - - - match: (?<=\s{4})(iex).*?(>) + - match: (?<=\s{4})(iex)(?:\((.*?)\))?(>) captures: 1: keyword.other.iex.elixir - 2: keyword.other.iex-angle.elixir punctuation.definition.iex.begin.elixir + 2: constant.other.elixir + 3: keyword.other.iex-angle.elixir punctuation.definition.iex.begin.elixir push: - clear_scopes: true - meta_scope: meta.interpolation.elixir markup.raw.block.markdown markup.raw.block.elixir - - match: ^\s*(\.{3})(>) - scope: keyword.other.elixir - match: \n|$ scope: punctuation.definition.iex.end.elixir pop: true - include: core_syntax - - match: \s*(```)\s*(elixir)\b - captures: - 1: punctuation.definition.code-block.begin.markdown - 2: constant.other.language-name.elixir + - match: (?={{is_markdown_elixir}}) push: - - clear_scopes: 2 + - clear_scopes: true - meta_scope: meta.interpolation.elixir markup.raw.block.markdown markup.raw.block.elixir - - match: '```\n?' - scope: punctuation.definition.code-block.end.markdown + - match: \n|$ + scope: punctuation.section.arguments.end.elixir pop: true - # NB: with_prototype causes "25000 context sanity limit" error. - # - match: '' - # set: core_syntax - # with_prototype: - # - match: '```\n?' - # scope: punctuation.definition.code-block.end.markdown - # pop: true - include: core_syntax - merge_conflict: - # NB: should use with_prototype but the context limit seems to be exhausted. :( - - match: ^<<<<<<< - scope: source.git keyword.control.git - push: [existing_code_pop, if_eol_pop] - - existing_code_pop: - # - meta_content_scope: meta.existing_code.git - - match: ^======= - scope: source.git keyword.control.git - set: [incoming_code_pop, if_eol_pop] - - include: core_syntax - - incoming_code_pop: - # - meta_content_scope: meta.incoming_code.git - - match: ^>>>>>>> - scope: source.git keyword.control.git - set: if_eol_pop - - include: core_syntax + iex_dots: + - match: ^(\s*)(\.{3})(?:\((.*?)\))?(>) + comment: iex prompt continuation + captures: + 1: meta.string.elixir + 2: keyword.other.iex-dots.elixir + 3: constant.other.elixir + 4: keyword.other.iex-angle.elixir punctuation.definition.iex.begin.elixir comma_and_skip_ws: + - include: invalid_comma - match: \, scope: punctuation.separator.sequence.elixir push: invalid_comma_or_non_space_pop @@ -1612,61 +2054,75 @@ contexts: scope: punctuation.separator.arguments.elixir push: invalid_comma_or_non_space_pop + invalid_comma: + - match: (?=,\s*(?>[,;)]|%>|->(?!:)|(?>do|when){{no_id_key_suffix}}|{{closing_keyword}})) + push: invalid_comma_or_non_space_pop + invalid_comma_or_non_space_pop: - include: comment - match: \, scope: invalid.illegal.separator.elixir - include: if_non_space_pop + ## Operators + operator: - - match: '::' - scope: keyword.operator.colon.elixir - - match: <-|-> - scope: keyword.operator.arrow.elixir - - match: <> - scope: keyword.operator.binary-concat.elixir - - match: |<~>|>? - comment: operators with pipe precedence - scope: keyword.operator.pipe.elixir - - match: ---|\+\+\+ - scope: keyword.operator.reserved.elixir - - match: -- - scope: keyword.operator.list-diff.elixir - - match: \+\+ - scope: keyword.operator.list-concat.elixir - - match: \.\.\. - comment: can appear in @type declarations - scope: keyword.operator.ellipsis.elixir - - match: \.\. - scope: keyword.operator.range.elixir - - match: => - scope: keyword.operator.map-pair.elixir - - match: (?>\|\|\||&&&|\^\^\^|<<<|>>>|~~~) - scope: keyword.operator.bitwise.elixir - - match: \\\\ - scope: keyword.operator.default.elixir - - match: (?>[!=]=|[<>])=? - scope: keyword.operator.comparison.elixir - - match: xor{{not_id_key_suffix}} - scope: invalid.deprecated.operator.elixir - - match: (?>not|and|or){{not_id_key_suffix}}|!|\|\||&& - scope: keyword.operator.logical.elixir - - match: '[-+*/]' - scope: keyword.operator.arithmetic.elixir - - match: =~ - scope: keyword.operator.regex.elixir - - match: = - scope: keyword.operator.match.elixir - - match: ; - scope: keyword.operator.semicolon.elixir - - match: \^ - scope: keyword.operator.pin.elixir + - match: | + (?x)(?> # Ordered by likeliness of occurrence. + (=(?![=>~])) + | (->|<-) + | (|<~>|>?) # Operators with pipe precedence. + | ((?>[!=]=|[<>])(?![<>])=?) + | (=>) + | (<>) + | (-(?!-)|\+(?!\+)|//|[*/]) + | (\+\+(?!\+)) + | (\|(?!\|)) + | (::(?!:)) + | (\^(?!\^\^)) + | (\|\|(?!\|)|&&(?!&)|!) + | (\.\.\.) # Can appear in @type declarations. + | (\.\.) + | (=~) + | (\\\\) + | (<<<|>>>|&&&|\|\|\||~~~|\^\^\^) + | (---|\+\+\+) + | (--(?!-)) + | (;) + ) + captures: + 1: keyword.operator.match.elixir + 2: keyword.operator.arrow.elixir + 3: keyword.operator.pipe.elixir + 4: keyword.operator.comparison.elixir + 5: keyword.operator.map-pair.elixir + 6: keyword.operator.binary-concat.elixir + 7: keyword.operator.arithmetic.elixir + 8: keyword.operator.list-concat.elixir + 9: keyword.operator.union.elixir + 10: keyword.operator.colon.elixir + 11: keyword.operator.pin.elixir + 12: keyword.operator.logical.elixir + 13: keyword.operator.ellipsis.elixir + 14: keyword.operator.range.elixir + 15: keyword.operator.regex.elixir + 16: keyword.operator.default.elixir + 17: keyword.operator.bitwise.elixir + 18: keyword.operator.reserved.elixir + 19: keyword.operator.list-diff.elixir + 20: keyword.operator.semicolon.elixir # Separate to make @type matching possible. cons_operator: - - match: \| + - match: \|(?![:|>]) scope: keyword.operator.cons.elixir + dot_operator: + - match: \.(?!\.) + scope: punctuation.accessor.dot.elixir + + ## Literals + tuple: - match: \} scope: invalid.illegal.stray-closing-brace.elixir @@ -1677,8 +2133,21 @@ contexts: - match: \} scope: punctuation.section.sequence.end.elixir pop: true - - include: comma_and_skip_ws - - include: core_syntax + - include: core_syntax_or_if_closing_pop + + item_access: + - match: | + (?x) + (?<=["'\w\])}]|\w[?!]|%>) + (?){{identifier}})|({{identifier}}) + captures: + 1: variable.other.unused.elixir + 2: variable.other.elixir set: map_body_pop - include: map_body_pop @@ -1708,26 +2183,27 @@ contexts: - match: \{ scope: punctuation.section.mapping.begin.elixir set: - - meta_scope: meta.mapping.elixir - - match: \} - scope: punctuation.section.mapping.end.elixir - pop: true - - include: comma_and_skip_ws - - include: core_syntax + - include: map_closing_pop - include: cons_operator + - include: core_syntax_or_if_closing_pop + - include: if_closing_token_pop - include: if_non_space_or_eol_pop - parens: + paren: + - match: (?<=%>)(?=\() + push: arguments_pop - match: \) scope: invalid.illegal.stray-closing-parenthesis.elixir - match: \( - scope: punctuation.section.parens.begin.elixir + scope: punctuation.section.group.begin.elixir push: - meta_scope: meta.parens.elixir - match: \) - scope: punctuation.section.parens.end.elixir + scope: punctuation.section.group.end.elixir pop: true - - include: core_syntax + - include: core_syntax_or_if_closing_pop + + ## Alias/Require statement alias: - match: (?>alias|require){{no_suffix_then_arguments}} @@ -1743,104 +2219,65 @@ contexts: - match: (?=,) set: - include: alias_as_arg - - include: arg_comma_and_skip_ws - - include: arguments_closing_pop - - include: core_syntax + - include: arguments_rest_pop - include: core_syntax + # alias X, as: Y - match: \s? scope: punctuation.section.arguments.begin.elixir set: - # alias X, as: Y - meta_scope: meta.function-call.arguments.elixir - include: arguments_ws_closing_pop - match: '' push: - include: alias_names - match: (?=,) - set: alias_kw_args_pop - - include: if_closing_tokens_pop + set: + - include: alias_as_arg + - include: arg_comma_and_skip_ws + - include: if_ws_closing_token_or_eol_pop + - include: core_syntax + - include: if_ws_closing_token_or_eol_pop - include: core_syntax - alias_kw_args_pop: - - include: comments - - include: alias_as_arg - - include: arg_comma_and_skip_ws - - include: if_closing_tokens_pop - - include: core_syntax - alias_as_arg: - match: as(:)(?!:) scope: constant.other.keyword.elixir captures: 1: punctuation.definition.constant.elixir - push: [alias_as_name_pop, if_non_space_pop] + push: [alias_as_name_pop, invalid_comma_or_non_space_pop] alias_as_name_pop: - - match: '{{module_name}}{{not_id_key_suffix}}' - scope: meta.path.modules.elixir entity.name.namespace.elixir - - match: '' - set: - - include: if_closing_tokens_pop - - include: core_syntax - - include: if_non_space_pop + - match: '{{module_name}}{{no_key_suffix}}' + scope: entity.name.namespace.elixir + - include: if_non_space_or_eol_pop alias_names: - - match: (?=(?>{{module_name}}|__MODULE__){{not_id_key_suffix}}|:(?>{{atom_id}}|['"])) + - match: | + (?x)(?= + (?:(?<=%>)\.)? (?>{{module_name}}{{no_key_suffix}} | __MODULE__{{no_id_key_suffix}}) + | :(?>{{atom_symbol}}|['"]) + ) push: - - meta_scope: meta.path.modules.elixir - - include: alias_module_name_pop - - match: __MODULE__ - scope: variable.language.special-form.elixir - set: alias_dot_or_pop + - include: module_name + - include: dot_accessor + - include: unquote_call_pop + - include: special_form - include: atom_symbol - - include: alias_dot_or_pop - - alias_module_name_pop: - - match: '{{module_name}}{{not_id_key_suffix}}' - scope: constant.other.module.elixir - set: alias_dot_or_pop - - alias_dot_or_pop: - - match: \.(?!\.) - scope: meta.path.modules.elixir punctuation.accessor.elixir - set: alias_name_or_tuple_pop - - include: if_non_space_or_eol_pop + - include: if_non_space_or_eol_pop - alias_name_or_tuple_pop: - - meta_scope: meta.path.modules.elixir - - include: alias_module_name_pop - - match: \{ - scope: punctuation.section.braces.begin.elixir - set: - - meta_scope: meta.path.modules.elixir - - match: \} - scope: punctuation.section.braces.end.elixir - pop: true - - include: comma_and_skip_ws - - match: '{{module_name}}{{not_id_key_suffix}}' - scope: constant.other.module.elixir - push: alias_dot_or_pop - - include: comments - # NB: to avoid messing up the highlighting due to an unfinished alias - - include: if_non_space_pop - - include: if_non_space_or_eol_pop + ## Atoms atom_keyword: - - match: (?>\*\*|\[\]|\^\^|\.)(:)(?!:) - scope: invalid.illegal.atom-keyword.elixir - captures: - 1: punctuation.definition.constant.elixir - match: | (?x) - (?! ::: | %:(?:{{atom_id}}|['"]) ) # No ::: and %:XYZ{} (it's a map) - {{atom_symbol}}(:)(?!:) + (?! \?: | ::: | %:(?:{{atom_id}}|['"]) ) # No ::: and %:XYZ{} (it's a map) + (?>{{atom_symbol}}|(?<=%>)) (:) {{no_colon_suffix}} comment: keyword symbol scope: constant.other.keyword.elixir captures: - 1: punctuation.definition.constant.elixir - # Look for ':' behind the closing apostrophe. - # TODO: doesn't work for: "abc #{"xyz"}": ... - - match: (?=(["'])(?>\\.|(?!\1).)*\1:(?!:)) + 1: punctuation.definition.constant.end.elixir + # Look for ':' behind the closing double/single quote. + - match: (?x)(?=(["']) (?>\\. | (?!\1).)* \1:{{no_colon_suffix}}) comment: keyword string push: - match: (.) @@ -1853,65 +2290,123 @@ contexts: pop: true atom_symbol: - - match: (:){{atom_symbol}} - comment: atom symbols - scope: constant.other.symbol.elixir - captures: - 1: punctuation.definition.constant.begin.elixir - - match: :' - scope: punctuation.definition.constant.begin.elixir - push: - - meta_scope: constant.other.symbol.single-quoted.elixir - - match: \' - scope: punctuation.definition.constant.end.elixir - pop: true - - include: escaped_or_interpolated - - match: :" - scope: punctuation.definition.constant.begin.elixir + - match: :(?!:(?!:)) + scope: constant.other.symbol.elixir punctuation.definition.constant.begin.elixir push: - - meta_scope: constant.other.symbol.double-quoted.elixir - - match: \" - scope: punctuation.definition.constant.end.elixir + - match: (?=<%)|{{atom_symbol}} + scope: constant.other.symbol.elixir pop: true - - include: escaped_or_interpolated + - match: (["']) + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: constant.other.symbol.quoted.elixir + - match: \1 + scope: punctuation.definition.constant.end.elixir + pop: true + - include: escaped_or_interpolated + - include: if_empty_pop + + ## Captures capture: - - match: \&(?=\s*(?>{{module_name}}\s*\.(?!\.)\s*)*(?>{{identifier}}|{{operators}})\s*/\s*\d) + - match: (&)\s*(\d+)(?!\.(?![.\D])) + captures: + 1: punctuation.definition.capture.elixir constant.other.capture.elixir + 2: constant.other.capture.elixir + - match: \& scope: keyword.operator.capture.elixir push: - - match: (/)\s*(\d+) - captures: - 1: punctuation.accessor.slash.elixir - 2: constant.numeric.integer.decimal.elixir + - match: (?=(?>do|fn|true|false|nil){{no_id_key_suffix}}) pop: true - - include: module_names - - match: '{{identifier}}|{{operators}}' - scope: variable.other.capture.elixir - - match: (\&0)|(\&\d+)|(\&) + - include: if_closing_token_pop + - match: (?=\.(?!\.)) + set: + - include: dot_operator + - include: arguments_pop + - include: capture_name_pop + - include: id_or_operator_call_pop + - include: quoted_remote_call_pop + - include: quoted_member_pop + - match: (?={{member}}{{no_id_key_suffix}}) + push: id_member_pop + - include: tuple_call_pop + - include: if_closing_token_pop + - include: if_non_space_or_eol_pop + - include: capture_name_pop + - include: special_form + - match: (?={{identifier}}\s*\.(?!\.|\s*\()) + push: identifier_pop + - include: if_non_space_or_eol_pop + + capture_name_pop: + - match: | + (?x)(?= + (?> + {{module_name}}{{no_key_suffix}} + | :{{atom_symbol}} + | (?>:(["'])) (?>\\\\|\\\1|(?!\1).)*? (\1) + ) + \s* (?!\. (?!\. | \s*\()) + | (?>:{{atom_symbol}}) \s*\.\s* {{member}} \s*\( + ) + comment: exit if module/atom is not followed by `.` or `.(` + pop: true + - include: atom_symbol + - include: atom_keyword + - include: module_name + - include: unquote_call + - match: (/)\s*(\d+)(?!\.(?![.\D])) captures: - 1: invalid.illegal.capture.elixir - 2: constant.other.capture.elixir - 3: keyword.operator.capture.elixir + 1: punctuation.accessor.arity.elixir + 2: constant.numeric.arity.elixir + pop: true + - match: | + (?x) + (?>(/(?=/)|{{member}}) | (["'])((?>\\\\|\\\2|(?!\2).)*?)(\2)) + (?=\s*(/)\s*(\d+)(?!\.(?![.\D]))) + captures: + 1: variable.other.capture.elixir + 2: punctuation.definition.constant.begin.elixir + 3: variable.other.capture.elixir + 4: punctuation.definition.constant.end.elixir - sql_fragment: - - match: (fragment)\s*(\() + ## SQL + + sql_or_fragment: + - match: (?>(fragment)|(sql))(\() captures: 1: support.function.elixir - 2: punctuation.section.arguments.begin.elixir + 2: variable.function.elixir + 3: punctuation.section.arguments.begin.elixir push: - - include: comment - # TODO: add sigil strings? Or use ~Q"" for SQL similar to ~L"" for LiveView? - - match: (?=") - set: [sql_fragment_args_pop, sql_string_pop] - - match: (?=\S) - set: sql_fragment_args_pop + - meta_scope: meta.function-call.arguments.elixir + - match: (?=unquote\() + set: [arguments_rest_pop, sql_unquote_pop, unquote_pop] + - include: sql_or_fragment_args_pop + + sql_unquote_pop: + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: sql_or_fragment_args_pop + + sql_or_fragment_args_pop: + - match: (?=") + set: [arguments_rest_pop, sql_string_pop] + - include: comment + - match: (?=\S) + set: arguments_rest_pop sql_string_pop: - - match: '"""\n' - scope: string.quoted.double.elixir punctuation.definition.string.begin.elixir + - match: (""")(.*)\n + captures: + 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir set: + - meta_scope: meta.string.elixir - match: '"""' - scope: string.quoted.double.elixir punctuation.definition.string.end.elixir + scope: string.quoted.triple.double.elixir punctuation.definition.string.end.elixir pop: true - match: '' push: scope:source.ex.sql @@ -1922,6 +2417,7 @@ contexts: - match: \" scope: string.quoted.double.elixir punctuation.definition.string.begin.elixir set: + - meta_scope: meta.string.elixir - match: \" scope: string.quoted.double.elixir punctuation.definition.string.end.elixir pop: true @@ -1933,30 +2429,42 @@ contexts: - include: sql_or_elixir_escaped_pop sql_or_elixir_escaped_pop: + - include: interpolated_elixir + - include: line_continuation - match: (?=\\(?!\\?\?)) - # NB: using push instead of set here breaks highlighting for ST3. - set: + push: - meta_scope: source.ex.sql - match: (?=\\\\?\?) comment: let source.ex.sql match "\\?" pop: true - include: escaped_char - - include: empty_pop + - include: if_empty_pop - sql_fragment_args_pop: - - include: arguments_closing_pop - - include: arg_comma_and_skip_ws - - include: core_syntax + opaque_struct: + # Attempt highlighting e.g. #PID<0.1.0>, #Decimal<1.0> etc. + - match: '#(?={{module_name}}(?:\s*\.\s*{{module_name}})*<)' + scope: punctuation.section.mapping.elixir + comment: opaque struct + push: + - include: module_name + - include: dot_operator + - match: \< + scope: punctuation.section.mapping.begin.elixir + set: + - match: (?<=\S|^)\> + scope: punctuation.section.mapping.end.elixir + pop: true + # Text between <> brackets isn't always valid Elixir code. + # - include: core_syntax comment: - - match: (##).* - scope: comment.line.number-sign.section.elixir - captures: - 1: punctuation.definition.comment.elixir - - match: (#).* - scope: comment.line.number-sign.elixir - captures: - 1: punctuation.definition.comment.elixir + # - include: opaque_struct + - match: \# + scope: punctuation.definition.comment.elixir + push: + - meta_scope: comment.line.number-sign.elixir + - match: \n + pop: true comments: - match: (?=#) @@ -1965,131 +2473,364 @@ contexts: - include: if_non_space_pop char_literal: - - match: \?$ - scope: invalid.illegal.character-literal.elixir - - match: \?(?>\\(?>.|\n)|\S|(\s)) + - match: (\?$)|(\?)(?>\\[\S\s]|\S|(\s)) comment: character literal as an integer - scope: constant.numeric.elixir + scope: constant.numeric.char.elixir captures: 1: invalid.illegal.character-literal.elixir + 2: punctuation.definition.numeric.elixir + 3: invalid.illegal.character-literal.elixir built_ins: - match: | - (?x) - (?> - is_(?>atom|binary|bitstring|boolean|float|function|integer|list|map|nil|number|pid|port|record|reference|tuple|exception) | - abs|bit_size|byte_size|div|elem|hd|length|map_size|node|rem|round|tl|trunc|tuple_size + (?x)(?> # Ordered by likeliness of occurrence. + is_(?> + binary|list|atom|integer|function|nil|map(?:_key)?|tuple|number + | boolean|float|bitstring|pid|exception|port|reference|struct + ) + | inspect|to_string|elem|length|apply|self|byte_size|div|send|rem|put_(?>elem|in) + | (?>function|macro)_exported\?|update_in|binary_part|hd|struct!?|max|trunc|then + | to_charlist|get(?:_and_update)?_in|map_size|min|tl|node|make_ref|bit_size + | tuple_size|spawn(?>_link|_monitor)?|pop_in|abs|round|sum|floor|ceil|tap ){{no_suffix_then_arguments}} scope: variable.function.built-in.elixir push: arguments_paren_or_ws_pop + ## Arguments arguments_paren_or_ws_pop: - include: arguments_pop - match: '' - set: [arguments_ws_closing_pop, arguments_ws_pop] + set: [arguments_ws_closing_or_empty_pop, arguments_ws_pop] arguments_or_pop: - match: '{{has_arguments}}' set: arguments_paren_or_ws_pop - - include: empty_pop + - include: if_empty_pop arguments_pop: - match: \( scope: punctuation.section.arguments.begin.elixir set: - meta_scope: meta.function-call.arguments.elixir - - include: arguments_closing_pop - - include: arg_comma_and_skip_ws - - include: core_syntax + - include: arguments_rest_pop arguments_closing_pop: - match: \) scope: punctuation.section.arguments.end.elixir pop: true - arguments_ws_closing_pop: - - match: \n|\s?(?=\s*(?>[\n})\];]|$|%>|{{closing_keywords}})) - scope: punctuation.section.arguments.end.elixir + map_closing_pop: + - match: \} + scope: meta.mapping.elixir punctuation.section.mapping.end.elixir pop: true - - match: (?<=::\bend\b)|(?<=([^:])\bend\b)|(?<=^end\b) + + arguments_rest_pop: + - include: arguments_closing_pop + - include: if_closing_token_pop + - include: arg_comma_and_skip_ws + - include: core_syntax + + arguments_ws_closing_pop: + - match: | + (?x) + \n | \s? (?=\s*{{closing_token}}) + | (?<=::\bend\b) | (?<=([^:])\bend\b) | (?<=^end\b) scope: punctuation.section.arguments.end.elixir pop: true + arguments_ws_closing_or_empty_pop: + - include: arguments_ws_closing_pop + - include: if_empty_pop + arguments_ws_pop: - match: \s? comment: arguments list without parentheses scope: punctuation.section.arguments.begin.elixir set: - meta_scope: meta.function-call.arguments.elixir - - match: (\,)(?=\s*do{{not_id_key_suffix}}) - scope: invalid.illegal.separator.elixir - - include: arg_comma_and_skip_ws - - include: do_block_pop - - match: | - (?x) - (?= - (?> - @\s*{{identifier}} | - {{identifier}}(?:\s*\.\s*{{identifier}})* - ) - \s*do{{not_id_key_suffix}} - ) - comment: prevent matching last argument as a function call - push: - - match: (@)\s*({{identifier}}) - comment: reference - captures: - 1: keyword.operator.attribute.elixir - 2: variable.other.attribute.elixir - - include: elixir_keywords - - include: special_form - # Avoid matching "and"/"or" as an id. - - include: operator - - match: _{{identifier}}? - scope: variable.other.unused.elixir - - match: '{{identifier}}' - scope: variable.other.elixir - - match: \.(?!\.) - scope: punctuation.accessor.dot.elixir - - include: empty_pop - - include: if_closing_tokens_pop - - include: core_syntax + - include: arguments_ws_rest_pop + + arguments_ws_rest_pop: + - include: invalid_comma + - include: arg_comma_and_skip_ws + - match: (?=\?:\s*{{closing_token}}) + set: + - include: char_literal + - include: if_ws_closing_token_or_eol_pop + - include: atom_keyword + - match: (?=(?>when|and|in|or|not){{no_id_key_suffix}}) + push: + - include: if_closing_token_pop + - match: (?!(?>when|and|in|or|not){{no_id_key_suffix}}|[,\s]|(?(?!>(?!>)) | \.\.(?!\.) | ::(?!:)) + push: + - include: if_closing_token_pop + - include: operator + - include: invalid_comma_or_non_space_pop + - match: (?<=[^:]:)\s + push: invalid_comma_or_non_space_pop + - include: do_block_pop + - include: fn_block + - include: if_ws_closing_token_or_eol_pop + - include: elixir_keywords + - include: special_form + - include: last_id_argument + - include: core_syntax + + last_id_argument: + - match: | + (?x)(?= + (?> + (?:@\s*)?{{identifier}} + | \.\s*(?:{{member}} | "(?>\\[\\"]|.)*?" | '(?>\\[\\']|.)*?') + ) + \s*do{{no_id_key_suffix}} + ) + comment: prevent matching last argument as a function call, e.g. `func first, second, last do end` + push: + - match: (?=do{{no_id_key_suffix}}) + pop: true + - match: (@)\s*({{identifier}}) + comment: reference + captures: + 1: keyword.operator.attribute.elixir + 2: variable.other.constant.elixir + - include: identifier_pop + - match: \.(?!\.) + scope: punctuation.accessor.dot.elixir + set: + - include: id_member_pop + - include: quoted_member_pop + - include: if_non_space_or_eol_pop + - include: if_non_space_or_eol_pop special_form: - - match: __(?>MODULE|ENV|DIR|CALLER|STACKTRACE)__{{not_id_key_suffix}} + - match: __(?>MODULE|ENV|DIR|CALLER|STACKTRACE)__{{no_id_key_suffix}} scope: variable.language.special-form.elixir elixir_keywords: - # TODO: separate and assign different scopes - - match: (?>when|not|in|or|and|fn|catch|after|rescue|do|else|end)\b(?![?!]) + - match: when\b(?![?!]) + scope: keyword.operator.when.elixir + comment: for `when`-clauses that are placed below the function header + push: + - match: (?=(?><-|->)(?!:)) + pop: true + - include: arguments_ws_rest_pop + - match: | + (?x)(?> # Ordered by likeliness of occurrence. + # (do) | (fn) # Handled by do_block and fn_block. + # (when) + (true|false|nil) + | (and|in|or|not) + | (else) + | (after) + | (rescue) + | (catch) + | (end) # Normally in EEx: <% end %> + )\b(?![?!]) comment: fully reserved keywords - scope: keyword.other.elixir - - match: (?>nil|true|false)\b(?![?!]) - scope: constant.language.elixir + captures: + 1: constant.language.elixir + 2: keyword.operator.logical.elixir + 3: keyword.control.conditional.else.elixir + 4: keyword.control.exception.after.elixir + 5: keyword.control.exception.rescue.elixir + 6: keyword.control.exception.catch.elixir + 7: keyword.context.block.end.elixir + + case_macro_call: + - match: case(?!\(){{no_suffix_then_arguments}} + scope: keyword.control.conditional.elixir + push: + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir + push: + - meta_scope: meta.block.elixir + - include: arrow_clauses_body_pop + - include: last_arg_block_end_pop + - include: arguments_ws_rest_pop + + one_left_arrow_clause: + - match: (?=\S) + push: + - match: (?=<-(?!:)|,|(?>when|do){{no_id_key_suffix}}|do:(?!:)) + set: + - match: (?=,|do{{no_id_key_suffix}}|do:(?!:)) + pop: true + - include: arguments_ws_rest_pop + - include: parameters_or_if_closing_pop + + with_macro_call: + - match: with(?!\(){{no_suffix_then_arguments}} + scope: keyword.control.conditional.elixir + push: + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - match: (?=do:(?!:)) + set: [arguments_ws_closing_pop, arguments_ws_rest_pop] + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir + set: + - meta_scope: meta.block.elixir + - match: else{{no_id_key_suffix}} + scope: keyword.control.conditional.else.elixir + push: arrow_clauses_body_pop + - include: last_arg_block_end_pop + - include: core_syntax + - include: arguments_ws_closing_pop + - include: arg_comma_and_skip_ws + - include: one_left_arrow_clause + + for_macro_call: + - match: for(?!\(){{no_suffix_then_arguments}} + scope: keyword.control.loop.for.elixir + push: + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - match: (?=reduce:(?!:)) + set: + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir + set: + - meta_scope: meta.block.elixir + - include: last_arg_block_end_pop + - match: '' + push: arrow_clauses_body_pop + - include: arg_comma_and_skip_ws + - include: arguments_ws_rest_pop + - match: (?={{atom_symbol}}:(?!:))|(?<=[^:]:) + set: [arguments_ws_closing_pop, arguments_ws_rest_pop] + - include: arguments_ws_closing_pop + - include: arg_comma_and_skip_ws + - include: one_left_arrow_clause + + try_macro_call: + - match: try(?!\(){{no_suffix_then_arguments}} + scope: keyword.control.exception.try.elixir + push: + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir + set: + - meta_scope: meta.block.elixir + - include: error_handling_clauses + - include: last_arg_block_end_pop + - include: core_syntax + - include: arguments_ws_closing_pop + - include: arg_comma_and_skip_ws + - include: core_syntax + + receive_macro_call: + - match: receive(?!\(){{no_suffix_then_arguments}} + scope: keyword.control.loop.receive.elixir + push: + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - match: do{{no_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.do.elixir + set: + - meta_scope: meta.block.elixir + - match: after{{no_id_key_suffix}} + scope: keyword.control.exception.catch.elixir + push: arrow_clauses_body_pop + - include: last_arg_block_end_pop + - match: '' + push: arrow_clauses_body_pop + - include: arguments_ws_closing_pop + - include: arg_comma_and_skip_ws + - include: core_syntax elixir_functions: - # TODO: separate and assign different scopes - - match: (?>case|for|if|cond|unless|try|receive|defrecord|defstruct|defexception|defoverridable|exit|raise|reraise|throw|import|require|use|using|quote|unquote|super|with){{no_suffix_then_arguments}} - scope: keyword.other.elixir + - include: unquote_call + - include: case_macro_call + - include: with_macro_call + - include: for_macro_call + - include: try_macro_call + - include: receive_macro_call + - match: | + (?x)(?> # Ordered by likeliness of occurrence. + (case|if|with|cond|unless) + | (use|import|require) + | (quote) + | (raise|reraise|throw) + | (for) + | (defstruct|defexception|defoverridable) + | (try) + | (receive) + | (exit) + | (super) + | (destructure) + ){{no_suffix_then_arguments}} + captures: + 1: keyword.control.conditional.elixir + 2: keyword.control.import.elixir + 3: keyword.other.quote.elixir + 4: keyword.control.flow.throw.elixir + 5: keyword.control.loop.for.elixir + 6: keyword.declaration.elixir + 7: keyword.control.exception.try.elixir + 8: keyword.control.loop.receive.elixir + 9: keyword.control.flow.exit.elixir + 10: keyword.other.super.elixir + 11: keyword.other.destructure.elixir push: arguments_paren_or_ws_pop + - include: defrecord + + unquote_pop: + - match: unquote(?:_splicing)? + scope: keyword.other.unquote.elixir + pop: true + + unquote_call_pop: + - match: (?=unquote(?:_splicing)?\() + set: [unquote_post_args_pop, arguments_pop, unquote_pop] + - match: (?=unquote(?:_splicing)?{{no_suffix_then_arguments}}) + set: [arguments_ws_pop, unquote_pop] + + unquote_post_args_pop: + - match: (?=\() + set: arguments_pop + - include: if_empty_pop unquote_call: - - match: unquote(?=\s*\() - scope: keyword.other.elixir - push: arguments_paren_or_ws_pop - - spaces_pop: - - match: \s* + - match: (?=unquote(?:_splicing)?{{no_suffix_then_arguments}}) + push: unquote_call_pop + + line_continuation: + - match: \\\n + scope: punctuation.separator.continuation.elixir + + ## Helpers + + if_closing_token_pop: + - match: (?=[)}\]]|>>(?!>)|%>|{{closing_keyword}}) pop: true - if_closing_tokens_no_eol_pop: - - match: (?=\s*(?>[})\];]|%>|{{closing_keywords}})) + if_ws_closing_token_pop: + - match: (?=\s*(?>[)}\];]|>>(?!>)|%>|{{closing_keyword}})) pop: true - if_closing_tokens_pop: - - include: if_closing_tokens_no_eol_pop - - include: if_eol_pop + if_ws_closing_token_or_eol_pop: + - include: if_ws_closing_token_pop + - match: (?=\s*(?>#|$)) + pop: true if_closing_paren_pop: - match: (?=\)) @@ -2103,10 +2844,6 @@ contexts: - match: (?=\S|$) pop: true - if_eol_pop: - - match: $ - pop: true - - empty_pop: + if_empty_pop: - match: '' pop: true diff --git a/Elixir/HTML (EEx).sublime-syntax b/Elixir/HTML (EEx).sublime-syntax index 3070d8b990..96e4c74ca9 100644 --- a/Elixir/HTML (EEx).sublime-syntax +++ b/Elixir/HTML (EEx).sublime-syntax @@ -15,24 +15,33 @@ contexts: eex_tags: - match: <%# + scope: punctuation.definition.comment.begin.eex push: - - meta_scope: comment.eex + - meta_scope: text.html.eex comment.block.eex - match: '%>' + scope: punctuation.definition.comment.end.eex pop: true - match: (?=<%) push: - clear_scopes: true - meta_scope: meta.interpolation.eex - - match: '%>' - scope: text.html.eex keyword.other.eex punctuation.section.embedded.end.eex + - match: (%)(>) + scope: text.html.eex + captures: + 1: entity.name.tag.eex + 2: punctuation.section.embedded.end.eex pop: true - - match: <%(?>%=?|[=/|]?) - scope: text.html.eex keyword.other.eex punctuation.section.embedded.begin.eex + # Tags <%/ and <%| are parsed but have no functionality in EEx yet. + - match: (<)(%(?>%=?|[=/|]?)) + scope: text.html.eex + captures: + 1: punctuation.section.embedded.begin.eex + 2: entity.name.tag.eex embed: scope:source.elixir embed_scope: text.html.eex source.elixir.embedded.eex escape: (?=%>|<%) - + # NB: can't use with_prototype because the following error dialog window appears: # ``` # Error loading syntax file "...": @@ -43,7 +52,7 @@ contexts: # https://github.com/phoenixframework/phoenix/blob/master/installer/templates/phx_live/templates/layout/root.html.leex # - match: <%(?>%=?|[=/|]?) - # scope: text.html.eex keyword.other.eex punctuation.section.embedded.begin.eex + # scope: text.html.eex punctuation.section.embedded.begin.eex # push: scope:source.eex # with_prototype: # - match: (?=%>) diff --git a/Elixir/PCRE (Erlang).sublime-syntax b/Elixir/PCRE (Erlang).sublime-syntax new file mode 100644 index 0000000000..107d456c1b --- /dev/null +++ b/Elixir/PCRE (Erlang).sublime-syntax @@ -0,0 +1,480 @@ +%YAML 1.2 +--- +name: PCRE (Erlang) +scope: source.pcree +file_extensions: [pcree] +hidden: true +comment: Perl Compatible Regular Expressions with a focus on Erlang (http://erlang.org/doc/man/re.html) +authors: [Aziz Köksal ] + +variables: + character_quantifier: '[?*+]' + lazy_or_possessive: '[?+]' + ranged_quantifier: '{\d+,?\d*?}' + capture_name: '[a-zA-Z_][a-zA-Z_\d]{,31}' + invalid_capture_name: '[^\[\\(){}|^$.?*+\n]+' + +contexts: + main: + - include: unexpected_quantifier + - match: (?=.|\n) + push: expression + + expression: + - include: quoted_sequence + - include: subroutine_call + - include: back_reference + - include: assertion + - include: comment + - include: escape_sequence + - include: class_set + - include: inline_option + - include: backtracking_verb + - include: group + - include: operator + - include: quantifier + - include: dot_meta_char + - include: literal + + quoted_sequence: + - match: \\Q + scope: keyword.control.quote.pcree punctuation.definition.quote.begin.pcree + push: + - meta_scope: meta.quote.pcree + - match: \\E + scope: keyword.control.quote.pcree punctuation.definition.quote.end.pcree + pop: true + - include: literal + + subroutine_call: + - match: |- + (?x) + \\g(?: + <( ((?>-[1-9]\d*|\d+) | {{capture_name}}) | \g<-1>?({{invalid_capture_name}}) )> | '\g<1>' | + (<({{invalid_capture_name}}*)>? | '\g<-1>'?) ) + scope: keyword.other.subroutine.pcree + captures: + 1: '' + 2: variable.other.subroutine.pcree + 3: invalid.illegal.subroutine.pcree + 4: invalid.illegal.subroutine.pcree + 5: invalid.illegal.subroutine.pcree + 6: invalid.illegal.subroutine.pcree + - match: |- + (?x) + \(\?(?: + ([+-]?\d+|R|(?:P>|&){{capture_name}})\) | + (?:P>|&)({{invalid_capture_name}})\)? | + (R|P>|&)\)? ) + scope: keyword.other.subroutine.pcree + captures: + 1: variable.other.subroutine.pcree + 2: invalid.illegal.subroutine.pcree + 3: invalid.illegal.subroutine.pcree + + back_reference: + - match: \\(?!0\d)\d+ + scope: keyword.other.back-reference.pcree + - match: |- + (?x) + \\g(?: + (-?[1-9]\d*) | + {((?:-?[1-9]\d*|{{capture_name}}))} | + {\g<-1>?({{invalid_capture_name}})} | + ({{{invalid_capture_name}}*}?|-?0) ) | + (\\g) + scope: keyword.other.back-reference.pcree + captures: + 1: variable.other.back-reference.pcree + 2: variable.other.back-reference.pcree + 3: invalid.illegal.back-reference.pcree + 4: invalid.illegal.back-reference.pcree + 5: invalid.illegal.back-reference.pcree + - match: (?x) \(\?P=({{capture_name}})\) | \(\?(P=)\) | \(\?P=({{invalid_capture_name}})\)? | \(\?(P=) + scope: keyword.other.back-reference.pcree + captures: + 1: variable.other.back-reference.pcree + 2: invalid.illegal.back-reference.pcree + 3: invalid.illegal.back-reference.pcree + 4: invalid.illegal.back-reference.pcree + - match: |- + (?x) + \\k(?: + {(? ({{capture_name}}) | \g<-1>?({{invalid_capture_name}}) )} | <\g> | '\g' | + ({({{invalid_capture_name}}*)}? | <\g<-1>>? | '\g<-1>'?) ) | + (\\k) + scope: keyword.other.back-reference.pcree + captures: + 1: '' # Ignore (?) + 2: variable.other.back-reference.pcree + 3: invalid.illegal.back-reference.pcree + 4: invalid.illegal.back-reference.pcree + 5: invalid.illegal.back-reference.pcree + 6: invalid.illegal.back-reference.pcree + + common_escape_sequence: + - match: \\(?i)[dhsvw] + scope: constant.other.escape-sequence.pcree + - include: character_property + - include: escaped_char + + character_property: + - match: \\[pP]{\^?(?>C[cfnos]?|L[&lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?|X(?>an|ps|sp|wd|uc))} + scope: constant.other.escape-sequence.general-category.pcree + - match: \\[pP][CLMNPSZ] + scope: constant.other.escape-sequence.general-category.pcree + - match: \\[pP]{[[:alpha:]]+} + scope: constant.other.escape-sequence.script-name.pcree + - match: \\[pP](?:({})|{({{invalid_capture_name}}*)}?)|(\\[pP]) + scope: constant.other.escape-sequence.general-category.pcree + captures: + 1: invalid.illegal.general-category.pcree + 2: invalid.illegal.general-category.pcree + 3: invalid.illegal.general-category.pcree + + # Outside character class. + escape_sequence: + - match: \\K + scope: constant.character.escape.match-reset.pcree + push: unexpected_quantifier_pop + - match: \\R + scope: constant.character.escape.unicode-newline.pcree + - match: \\X + scope: constant.character.escape.extended-grapheme-cluster.pcree + - include: common_escape_sequence + + operator: + - match: \| + scope: keyword.operator.alternation.pcree + push: unexpected_quantifier_pop + + assertion: + - match: \^ + scope: keyword.control.anchor.line-begin.pcree + push: unexpected_quantifier_pop + - match: \$ + scope: keyword.control.anchor.line-end.pcree + push: unexpected_quantifier_pop + # Simple assertions. + - match: \\[bBAzZG] + scope: keyword.control.anchor.simple.pcree + push: unexpected_quantifier_pop + + escaped_char: + - match: \\{{character_quantifier}} + scope: constant.character.escape.quantifier.pcree + - match: \\\. + scope: constant.character.escape.dot.pcree + - match: \\x\h\h|(\\x(?!{))|(?>\\x(?>{\h+}|{(?>\h|([^}]))+}|({}?)|)) + scope: constant.character.escape.hex.pcree + captures: + 1: invalid.illegal.escape.hex.pcree + 2: invalid.illegal.escape.hex.pcree + 3: invalid.illegal.escape.hex.pcree + - match: \\0[0-7]{1,2} + scope: constant.character.escape.octal.pcree + - match: (\\o(?!{))|(?>\\o(?>{[0-7]+}|{(?>[0-7]|([^}]))+}|({}?)|)) + scope: constant.character.escape.octal.pcree + captures: + 1: invalid.illegal.escape.octal.pcree + 2: invalid.illegal.escape.octal.pcree + 3: invalid.illegal.escape.octal.pcree + - match: \\c + scope: constant.character.escape.ascii.pcree + push: + - match: \p{ascii}|(.?) + scope: constant.character.escape.ascii.pcree + captures: + 1: invalid.illegal.escape.ascii.pcree + pop: true + - match: \\[aefnrt] + scope: constant.character.escape.non-printable.pcree + - match: \\. + scope: constant.character.escape.pcree + + inline_option: + - match: (\(\*)((?>NO_START_OPT|UTF8?|UCP|CRLF|CR|LF|ANYCRLF|ANY|BSR_ANYCRLF|BSR_UNICODE|LIMIT_(?>MATCH|RECURSION)=)) + captures: + 1: punctuation.definition.annotation.begin.pcree + 2: storage.modifier.mode.letter.pcree + push: + - meta_scope: keyword.control.flag.pcree + - match: (?<==)\d+ + scope: constant.numeric.integer.decimal.pcree + - match: '[^)]+' + scope: invalid.illegal.inline-option.pcree + - match: \) + scope: punctuation.definition.annotation.end.pcree + set: unexpected_quantifier_pop + + backtracking_verb: + - match: \(\*(?=[a-zA-Z:]) + scope: punctuation.definition.annotation.begin.pcree + push: + - meta_scope: meta.backtracking.pcree + - match: ((?>ACCEPT|COMMIT|FAIL|F))(:?) + captures: + 1: keyword.control.verb.pcree + 2: punctuation.separator.sequence.pcree + set: backtracking_verb_end + - match: MARK(?=:\)) + scope: keyword.control.verb.pcree + set: backtracking_verb_end + - match: (?>MARK|THEN|PRUNE|SKIP|(?=:[^)])) + scope: keyword.control.verb.pcree + set: + - match: ':' + scope: punctuation.separator.sequence.pcree + push: + - meta_scope: meta.backtracking.pcree + - meta_content_scope: entity.name.constant.pcree + - match: (?=\)) + pop: true + - include: backtracking_verb_end + - include: backtracking_verb_end + + backtracking_verb_end: + - match: '' + set: + - meta_scope: meta.backtracking.pcree + - match: '[^)\n]+' + scope: invalid.illegal.backtracking-verb.pcree + - match: \) + scope: punctuation.definition.annotation.end.pcree + set: unexpected_quantifier_pop + + group: + - match: \) + scope: invalid.illegal.unmatched-brace.pcree + # Comment + - match: \(\?# + scope: punctuation.definition.comment.begin.pcree + push: + - meta_scope: meta.group.pcree comment.block.group.pcree + - match: \) + scope: punctuation.definition.comment.end.pcree + set: unexpected_quantifier_pop + - match: \( + scope: keyword.control.group.pcree punctuation.definition.group.begin.pcree + push: + - meta_scope: meta.group.pcree + # Look-ahead and look-behind. + - match: \?P?<({{capture_name}})> | '\g<-1>' | (P)(?=') | P?(<>|'') | P?<({{invalid_capture_name}}?)> | P?'\g<-1>') + scope: keyword.other.named-capture-group.pcree + captures: + 1: entity.name.capture-group.pcree + 2: invalid.illegal.named-capture.pcree + 3: invalid.illegal.named-capture.pcree + 4: invalid.illegal.named-capture.pcree + set: [group_body, unexpected_quantifier_pop] + # Atomic group. + - match: \?> + scope: keyword.control.atomic-group.pcree + set: [group_body, unexpected_quantifier_pop] + # Non-capturing group. + - match: '\?:' + scope: keyword.control.non-capturing-group.pcree + set: [group_body, unexpected_quantifier_pop] + # Reset/overload group numbers inside. + - match: \?\| + scope: keyword.control.reset-numbers-group.pcree + set: [group_body, unexpected_quantifier_pop] + # Internal option setting. + - match: (?x) (\?) ([imsxJUX]+(?:-[imsxJUX]*)? | (?:-[imsxJUX]*)+) (.*?) (:|(?=\))) + scope: storage.modifier.mode.pcree + captures: + 1: storage.modifier.mode.question.pcree + 2: storage.modifier.mode.letters.pcree + 3: invalid.illegal.inline-option.pcree + 4: storage.modifier.mode.colon.pcree + set: [group_body, unexpected_quantifier_pop] + - match: (?=\?\() + set: conditional_subpattern_pop + - match: '' + set: [group_body, unexpected_quantifier_pop] + + group_body: + - meta_content_scope: meta.group.pcree + - match: \) + scope: meta.group.pcree keyword.control.group.pcree punctuation.definition.group.end.pcree + pop: true + - include: expression + + conditional_subpattern_pop: + - meta_content_scope: meta.group.pcree + - include: conditional_subpattern + - match: '' + pop: true + + conditional_subpattern: + - match: \?((\()(\))) + scope: keyword.control.conditional.pcree + captures: + 1: invalid.illegal.conditional.pcree + 2: punctuation.definition.conditional.begin.pcree + 3: punctuation.definition.conditional.end.pcree + set: [group_body, unexpected_quantifier_pop] + - match: \?(\() + scope: keyword.control.conditional.pcree + captures: + 1: punctuation.definition.conditional.begin.pcree + push: + - meta_scope: meta.conditional.pcree + # Pseudo-condition called DEFINE. + - match: DEFINE(?=\)) + scope: keyword.other.conditional.definition.pcree + set: conditional_subpattern_end + # References to recursion. + - match: (R)(?:(&)({{capture_name}})|(\d+)|(?=\))) + captures: + 1: keyword.operator.recursion.pcree + 2: keyword.operator.recursion.pcree + 3: variable.other.recursion.pcree + 4: variable.other.recursion.pcree + set: conditional_subpattern_end + # References to subpatterns. + - match: <({{capture_name}})>|'\g<-1>'|\g<-1> + captures: + 1: variable.other.back-reference.pcree + set: conditional_subpattern_end + # Assertions: positive or negative lookahead or lookbehind assertion. + - match: \?) + push: + - match: (\\[dsw])|(\\x\h\h?|\\x{\h+}|\\[0-7]{1,3}|\o{[0-7]+}|\\c\p{ascii}|\\.|[^\\]) + scope: meta.character-range.pcree + captures: + 1: invalid.illegal.range.pcree + 2: constant.other.range.pcree + set: + - match: '-' + scope: keyword.operator.range.pcree + set: + - meta_scope: meta.character-range.pcree + - match: (\\[dsw])|(\\x\h\h?|\\x{\h+}|\\[0-7]{1,3}|\o{[0-7]+}|\\c\p{ascii}|\\.|[^\\]) + captures: + 1: invalid.illegal.range.pcree + 2: constant.other.range.pcree + pop: true + + # Escape sequences inside [...] + class_set_escape_sequence: + - match: \\N + scope: invalid.illegal.escape-sequence.pcree + - match: \\b + scope: constant.character.escape.backspace.pcree + # Inside a class set \x, \xh and \xhh are valid sequences. + - match: \\x\h?\h? + scope: constant.character.escape.hex.pcree + - include: common_escape_sequence + + # E.g: [:alpha:] or [:^alpha:] + posix_character_class: + - match: \[:[<>]:\] + scope: invalid.deprecated.word-boundary.pcree + - match: \[::\] + scope: invalid.illegal.posix-class.pcree + # Positive look-ahead for :] because [: can stand alone without being a posix character class. + # Read as: in-between there may be 0 or more of "\\", "[" not followed by ":", "[" or "]" preceded by a "\", + # and any other character besides "[", "]". + - match: (?x) (\[:) (?=(\\\\ | \[(?!:) | (?<=\\)[\[\]] | [^\[\]])*? :]) + scope: keyword.control.set.posix.pcree punctuation.definition.posix-class.begin.pcree + push: + - meta_scope: constant.other.set.posix.pcree + - match: :] + scope: keyword.control.set.posix.pcree punctuation.definition.posix-class.end.pcree + pop: true + - match: (\^?)((?>alnum|alpha|ascii|blank|cntrl|digit|graph|lower|print|punct|space|upper|word|xdigit)) + captures: + 1: keyword.control.set.negation.pcree + 2: constant.other.posix-class.name.pcree + - match: .+?(?=:]) + scope: invalid.illegal.unknown-posix-class.pcree + + quantifier: + - match: ({)(\d+)(,)?(\d*)(}){{lazy_or_possessive}}? + scope: meta.quantifier.pcree keyword.operator.quantifier.pcree + captures: + 1: punctuation.definition.quantifier.begin.pcree + 2: constant.numeric.quantifier.min.pcree + 3: punctuation.separator.quantifier.pcree + 4: constant.numeric.quantifier.max.pcree + 5: punctuation.definition.quantifier.end.pcree + push: unexpected_quantifier_pop + - match: '{{character_quantifier}}{{lazy_or_possessive}}?' + scope: meta.quantifier.pcree keyword.operator.quantifier.pcree + push: unexpected_quantifier_pop + + unexpected_quantifier: + - match: (?>{{character_quantifier}}|{{ranged_quantifier}})+ + scope: invalid.illegal.unexpected-quantifier.pcree + + unexpected_quantifier_pop: + - include: unexpected_quantifier + - match: '' + pop: true + + dot_meta_char: + - match: \. + scope: keyword.other.any.pcree + + comment: + - match: '(?=(\\{2})*\\ #)' + push: + - include: escape_sequence + - match: '#' + scope: meta.literal.pcree + pop: true + - match: (?<=\s)# + push: + - meta_scope: comment.line.number-sign.pcree + - match: \n + pop: true + + literal: + - match: .|\n + scope: meta.literal.pcree diff --git a/Elixir/Regular Expressions (Elixir).sublime-syntax b/Elixir/Regular Expressions (Elixir).sublime-syntax deleted file mode 100644 index eab88eeed6..0000000000 --- a/Elixir/Regular Expressions (Elixir).sublime-syntax +++ /dev/null @@ -1,480 +0,0 @@ -%YAML 1.2 ---- -name: Regular Expressions (Elixir) -scope: source.regexp.elixir -file_extensions: [ex.re] -hidden: true -comment: Elixir uses Erlang's regular expressions (http://erlang.org/doc/man/re.html) -authors: [Aziz Köksal ] - -variables: - character_quantifier: '[?*+]' - lazy_or_possessive: '[?+]' - ranged_quantifier: '{\d+,?\d*?}' - capture_name: '[a-zA-Z_][a-zA-Z_\d]{,31}' - invalid_capture_name: '[^\[\\(){}|^$.?*+\n]+' - -contexts: - main: - - include: unexpected_quantifier - - match: (?=.|\n) - push: expression - - expression: - - include: quoted_sequence - - include: subroutine_call - - include: back_reference - - include: assertion - - include: comment - - include: escape_sequence - - include: class_set - - include: inline_option - - include: backtracking_verb - - include: group - - include: operator - - include: quantifier - - include: dot_meta_char - - include: literal - - quoted_sequence: - - match: \\Q - scope: keyword.control.quote.regexp.elixir punctuation.definition.quote.begin.regexp.elixir - push: - - meta_scope: meta.quote.regexp.elixir - - match: \\E - scope: keyword.control.quote.regexp.elixir punctuation.definition.quote.end.regexp.elixir - pop: true - - include: literal - - subroutine_call: - - match: |- - (?x) - \\g(?: - <( ((?>-[1-9]\d*|\d+) | {{capture_name}}) | \g<-1>?({{invalid_capture_name}}) )> | '\g<1>' | - (<({{invalid_capture_name}}*)>? | '\g<-1>'?) ) - scope: keyword.other.subroutine.regexp.elixir - captures: - 1: '' - 2: variable.other.subroutine.regexp.elixir - 3: invalid.illegal.subroutine.regexp.elixir - 4: invalid.illegal.subroutine.regexp.elixir - 5: invalid.illegal.subroutine.regexp.elixir - 6: invalid.illegal.subroutine.regexp.elixir - - match: |- - (?x) - \(\?(?: - ([+-]?\d+|R|(?:P>|&){{capture_name}})\) | - (?:P>|&)({{invalid_capture_name}})\)? | - (R|P>|&)\)? ) - scope: keyword.other.subroutine.regexp.elixir - captures: - 1: variable.other.subroutine.regexp.elixir - 2: invalid.illegal.subroutine.regexp.elixir - 3: invalid.illegal.subroutine.regexp.elixir - - back_reference: - - match: \\(?!0\d)\d+ - scope: keyword.other.back-reference.regexp.elixir - - match: |- - (?x) - \\g(?: - (-?[1-9]\d*) | - {((?:-?[1-9]\d*|{{capture_name}}))} | - {\g<-1>?({{invalid_capture_name}})} | - ({{{invalid_capture_name}}*}?|-?0) ) | - (\\g) - scope: keyword.other.back-reference.regexp.elixir - captures: - 1: variable.other.back-reference.regexp.elixir - 2: variable.other.back-reference.regexp.elixir - 3: invalid.illegal.back-reference.regexp.elixir - 4: invalid.illegal.back-reference.regexp.elixir - 5: invalid.illegal.back-reference.regexp.elixir - - match: (?x) \(\?P=({{capture_name}})\) | \(\?(P=)\) | \(\?P=({{invalid_capture_name}})\)? | \(\?(P=) - scope: keyword.other.back-reference.regexp.elixir - captures: - 1: variable.other.back-reference.regexp.elixir - 2: invalid.illegal.back-reference.regexp.elixir - 3: invalid.illegal.back-reference.regexp.elixir - 4: invalid.illegal.back-reference.regexp.elixir - - match: |- - (?x) - \\k(?: - {(? ({{capture_name}}) | \g<-1>?({{invalid_capture_name}}) )} | <\g> | '\g' | - ({({{invalid_capture_name}}*)}? | <\g<-1>>? | '\g<-1>'?) ) | - (\\k) - scope: keyword.other.back-reference.regexp.elixir - captures: - 1: '' # Ignore (?) - 2: variable.other.back-reference.regexp.elixir - 3: invalid.illegal.back-reference.regexp.elixir - 4: invalid.illegal.back-reference.regexp.elixir - 5: invalid.illegal.back-reference.regexp.elixir - 6: invalid.illegal.back-reference.regexp.elixir - - common_escape_sequence: - - match: \\(?i)[dhsvw] - scope: constant.other.escape-sequence.regexp.elixir - - include: character_property - - include: escaped_char - - character_property: - - match: \\[pP]{\^?(?>C[cfnos]?|L[&lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?|X(?>an|ps|sp|wd|uc))} - scope: constant.other.escape-sequence.general-category.regexp.elixir - - match: \\[pP][CLMNPSZ] - scope: constant.other.escape-sequence.general-category.regexp.elixir - - match: \\[pP]{[[:alpha:]]+} - scope: constant.other.escape-sequence.script-name.regexp.elixir - - match: \\[pP](?:({})|{({{invalid_capture_name}}*)}?)|(\\[pP]) - scope: constant.other.escape-sequence.general-category.regexp.elixir - captures: - 1: invalid.illegal.general-category.regexp.elixir - 2: invalid.illegal.general-category.regexp.elixir - 3: invalid.illegal.general-category.regexp.elixir - - # Outside character class. - escape_sequence: - - match: \\K - scope: constant.character.escape.match-reset.regexp.elixir - push: unexpected_quantifier_pop - - match: \\R - scope: constant.character.escape.unicode-newline.regexp.elixir - - match: \\X - scope: constant.character.escape.extended-grapheme-cluster.regexp.elixir - - include: common_escape_sequence - - operator: - - match: \| - scope: keyword.operator.alternation.regexp.elixir - push: unexpected_quantifier_pop - - assertion: - - match: \^ - scope: keyword.control.anchor.line-begin.regexp.elixir - push: unexpected_quantifier_pop - - match: \$ - scope: keyword.control.anchor.line-end.regexp.elixir - push: unexpected_quantifier_pop - # Simple assertions. - - match: \\[bBAzZG] - scope: keyword.control.anchor.simple.regexp.elixir - push: unexpected_quantifier_pop - - escaped_char: - - match: \\{{character_quantifier}} - scope: constant.character.escape.quantifier.regexp.elixir - - match: \\\. - scope: constant.character.escape.dot.regexp.elixir - - match: \\x\h\h|(\\x(?!{))|(?>\\x(?>{\h+}|{(?>\h|([^}]))+}|({}?)|)) - scope: constant.character.escape.hex.regexp.elixir - captures: - 1: invalid.illegal.escape.hex.regexp.elixir - 2: invalid.illegal.escape.hex.regexp.elixir - 3: invalid.illegal.escape.hex.regexp.elixir - - match: \\0[0-7]{1,2} - scope: constant.character.escape.octal.regexp.elixir - - match: (\\o(?!{))|(?>\\o(?>{[0-7]+}|{(?>[0-7]|([^}]))+}|({}?)|)) - scope: constant.character.escape.octal.regexp.elixir - captures: - 1: invalid.illegal.escape.octal.regexp.elixir - 2: invalid.illegal.escape.octal.regexp.elixir - 3: invalid.illegal.escape.octal.regexp.elixir - - match: \\c - scope: constant.character.escape.ascii.regexp.elixir - push: - - match: \p{ascii}|(.?) - scope: constant.character.escape.ascii.regexp.elixir - captures: - 1: invalid.illegal.escape.ascii.regexp.elixir - pop: true - - match: \\[aefnrt] - scope: constant.character.escape.non-printable.regexp.elixir - - match: \\. - scope: constant.character.escape.regexp.elixir - - inline_option: - - match: (\(\*)((?>NO_START_OPT|UTF8?|UCP|CRLF|CR|LF|ANYCRLF|ANY|BSR_ANYCRLF|BSR_UNICODE|LIMIT_(?>MATCH|RECURSION)=)) - captures: - 1: punctuation.definition.annotation.begin.regexp.elixir - 2: storage.modifier.mode.letter.regexp.elixir - push: - - meta_scope: keyword.control.flag.regexp.elixir - - match: (?<==)\d+ - scope: constant.numeric.integer.decimal.regexp.elixir - - match: '[^)]+' - scope: invalid.illegal.inline-option.regexp.elixir - - match: \) - scope: punctuation.definition.annotation.end.regexp.elixir - set: unexpected_quantifier_pop - - backtracking_verb: - - match: \(\*(?=[a-zA-Z:]) - scope: punctuation.definition.annotation.begin.regexp.elixir - push: - - meta_scope: meta.backtracking.regexp.elixir - - match: ((?>ACCEPT|COMMIT|FAIL|F))(:?) - captures: - 1: keyword.control.verb.regexp.elixir - 2: punctuation.separator.sequence.regexp.elixir - set: backtracking_verb_end - - match: MARK(?=:\)) - scope: keyword.control.verb.regexp.elixir - set: backtracking_verb_end - - match: (?>MARK|THEN|PRUNE|SKIP|(?=:[^)])) - scope: keyword.control.verb.regexp.elixir - set: - - match: ':' - scope: punctuation.separator.sequence.regexp.elixir - push: - - meta_scope: meta.backtracking.regexp.elixir - - meta_content_scope: entity.name.constant.regexp.elixir - - match: (?=\)) - pop: true - - include: backtracking_verb_end - - include: backtracking_verb_end - - backtracking_verb_end: - - match: '' - set: - - meta_scope: meta.backtracking.regexp.elixir - - match: '[^)\n]+' - scope: invalid.illegal.backtracking-verb.regexp.elixir - - match: \) - scope: punctuation.definition.annotation.end.regexp.elixir - set: unexpected_quantifier_pop - - group: - - match: \) - scope: invalid.illegal.unmatched-brace.regexp.elixir - # Comment - - match: \(\?# - scope: punctuation.definition.comment.begin.regexp.elixir - push: - - meta_scope: meta.group.regexp.elixir comment.block.group.regexp.elixir - - match: \) - scope: punctuation.definition.comment.end.regexp.elixir - set: unexpected_quantifier_pop - - match: \( - scope: keyword.control.group.regexp.elixir punctuation.definition.group.begin.regexp.elixir - push: - - meta_scope: meta.group.regexp.elixir - # Look-ahead and look-behind. - - match: \?P?<({{capture_name}})> | '\g<-1>' | (P)(?=') | P?(<>|'') | P?<({{invalid_capture_name}}?)> | P?'\g<-1>') - scope: keyword.other.named-capture-group.regexp.elixir - captures: - 1: entity.name.capture-group.regexp.elixir - 2: invalid.illegal.named-capture.regexp.elixir - 3: invalid.illegal.named-capture.regexp.elixir - 4: invalid.illegal.named-capture.regexp.elixir - set: [group_body, unexpected_quantifier_pop] - # Atomic group. - - match: \?> - scope: keyword.control.atomic-group.regexp.elixir - set: [group_body, unexpected_quantifier_pop] - # Non-capturing group. - - match: '\?:' - scope: keyword.control.non-capturing-group.regexp.elixir - set: [group_body, unexpected_quantifier_pop] - # Reset/overload group numbers inside. - - match: \?\| - scope: keyword.control.reset-numbers-group.regexp.elixir - set: [group_body, unexpected_quantifier_pop] - # Internal option setting. - - match: (?x) (\?) ([imsxJUX]+(?:-[imsxJUX]*)? | (?:-[imsxJUX]*)+) (.*?) (:|(?=\))) - scope: storage.modifier.mode.regexp.elixir - captures: - 1: storage.modifier.mode.question.regexp.elixir - 2: storage.modifier.mode.letters.regexp.elixir - 3: invalid.illegal.inline-option.regexp.elixir - 4: storage.modifier.mode.colon.regexp.elixir - set: [group_body, unexpected_quantifier_pop] - - match: (?=\?\() - set: conditional_subpattern_pop - - match: '' - set: [group_body, unexpected_quantifier_pop] - - group_body: - - meta_content_scope: meta.group.regexp.elixir - - match: \) - scope: meta.group.regexp.elixir keyword.control.group.regexp.elixir punctuation.definition.group.end.regexp.elixir - pop: true - - include: expression - - conditional_subpattern_pop: - - meta_content_scope: meta.group.regexp.elixir - - include: conditional_subpattern - - match: '' - pop: true - - conditional_subpattern: - - match: \?((\()(\))) - scope: keyword.control.conditional.regexp.elixir - captures: - 1: invalid.illegal.conditional.regexp.elixir - 2: punctuation.definition.conditional.begin.regexp.elixir - 3: punctuation.definition.conditional.end.regexp.elixir - set: [group_body, unexpected_quantifier_pop] - - match: \?(\() - scope: keyword.control.conditional.regexp.elixir - captures: - 1: punctuation.definition.conditional.begin.regexp.elixir - push: - - meta_scope: meta.conditional.regexp.elixir - # Pseudo-condition called DEFINE. - - match: DEFINE(?=\)) - scope: keyword.other.conditional.definition.regexp.elixir - set: conditional_subpattern_end - # References to recursion. - - match: (R)(?:(&)({{capture_name}})|(\d+)|(?=\))) - captures: - 1: keyword.operator.recursion.regexp.elixir - 2: keyword.operator.recursion.regexp.elixir - 3: variable.other.recursion.regexp.elixir - 4: variable.other.recursion.regexp.elixir - set: conditional_subpattern_end - # References to subpatterns. - - match: <({{capture_name}})>|'\g<-1>'|\g<-1> - captures: - 1: variable.other.back-reference.regexp.elixir - set: conditional_subpattern_end - # Assertions: positive or negative lookahead or lookbehind assertion. - - match: \?) - push: - - match: (\\[dsw])|(\\x\h\h?|\\x{\h+}|\\[0-7]{1,3}|\o{[0-7]+}|\\c\p{ascii}|\\.|[^\\]) - scope: meta.character-range.regexp.elixir - captures: - 1: invalid.illegal.range.regexp.elixir - 2: constant.other.range.regexp.elixir - set: - - match: '-' - scope: keyword.operator.range.regexp.elixir - set: - - meta_scope: meta.character-range.regexp.elixir - - match: (\\[dsw])|(\\x\h\h?|\\x{\h+}|\\[0-7]{1,3}|\o{[0-7]+}|\\c\p{ascii}|\\.|[^\\]) - captures: - 1: invalid.illegal.range.regexp.elixir - 2: constant.other.range.regexp.elixir - pop: true - - # Escape sequences inside [...] - class_set_escape_sequence: - - match: \\N - scope: invalid.illegal.escape-sequence.regexp.elixir - - match: \\b - scope: constant.character.escape.backspace.regexp.elixir - # Inside a class set \x, \xh and \xhh are valid sequences. - - match: \\x\h?\h? - scope: constant.character.escape.hex.regexp.elixir - - include: common_escape_sequence - - # E.g: [:alpha:] or [:^alpha:] - posix_character_class: - - match: \[:[<>]:\] - scope: invalid.deprecated.word-boundary.regexp.elixir - - match: \[::\] - scope: invalid.illegal.posix-class.regexp.elixir - # Positive look-ahead for :] because [: can stand alone without being a posix character class. - # Read as: in-between there may be 0 or more of "\\", "[" not followed by ":", "[" or "]" preceded by a "\", - # and any other character besides "[", "]". - - match: (?x) (\[:) (?=(\\\\ | \[(?!:) | (?<=\\)[\[\]] | [^\[\]])*? :]) - scope: keyword.control.set.posix.regexp.elixir punctuation.definition.posix-class.begin.regexp.elixir - push: - - meta_scope: constant.other.set.posix.regexp.elixir - - match: :] - scope: keyword.control.set.posix.regexp.elixir punctuation.definition.posix-class.end.regexp.elixir - pop: true - - match: (\^?)((?>alnum|alpha|ascii|blank|cntrl|digit|graph|lower|print|punct|space|upper|word|xdigit)) - captures: - 1: keyword.control.set.negation.regexp.elixir - 2: constant.other.posix-class.name.regexp.elixir - - match: .+?(?=:]) - scope: invalid.illegal.unknown-posix-class.regexp.elixir - - quantifier: - - match: ({)(\d+)(,)?(\d*)(}){{lazy_or_possessive}}? - scope: meta.quantifier.regexp.elixir keyword.operator.quantifier.regexp.elixir - captures: - 1: punctuation.definition.quantifier.begin.regexp.elixir - 2: constant.numeric.quantifier.min.regexp.elixir - 3: punctuation.separator.quantifier.regexp.elixir - 4: constant.numeric.quantifier.max.regexp.elixir - 5: punctuation.definition.quantifier.end.regexp.elixir - push: unexpected_quantifier_pop - - match: '{{character_quantifier}}{{lazy_or_possessive}}?' - scope: meta.quantifier.regexp.elixir keyword.operator.quantifier.regexp.elixir - push: unexpected_quantifier_pop - - unexpected_quantifier: - - match: (?>{{character_quantifier}}|{{ranged_quantifier}})+ - scope: invalid.illegal.unexpected-quantifier.regexp.elixir - - unexpected_quantifier_pop: - - include: unexpected_quantifier - - match: '' - pop: true - - dot_meta_char: - - match: \. - scope: keyword.other.any.regexp.elixir - - comment: - - match: '(?=(\\{2})*\\ #)' - push: - - include: escape_sequence - - match: '#' - scope: meta.literal.regexp.elixir - pop: true - - match: (?<=\s)# - push: - - meta_scope: comment.line.number-sign.regexp.elixir - - match: \n - pop: true - - literal: - - match: .|\n - scope: meta.literal.regexp.elixir diff --git a/Elixir/SQL (Elixir).sublime-syntax b/Elixir/SQL (Elixir).sublime-syntax index 61ce352c23..ec300eb17d 100644 --- a/Elixir/SQL (Elixir).sublime-syntax +++ b/Elixir/SQL (Elixir).sublime-syntax @@ -1,62 +1,86 @@ %YAML 1.2 --- -# NB: "extends" and "meta_prepend" are only supported by ST4. -# Not using them yet to still support ST3 for a while. name: SQL (Elixir) file_extensions: [ex.sql] scope: source.ex.sql -# extends: Packages/SQL/SQL.sublime-syntax contexts: main: - # - meta_prepend: true - match: | (?xi) - \b(?> - # Postgres jsonb functions. + (?> ( - jsonb_to_tsvector | to_jsonb? | (?>array|row)_to_json | - jsonb?_build_array | jsonb?(?:_build)?_object | - jsonb?_(?> - array_(?>length|elements(?:_text)?) | each(?:_text)? | - extract_path(?:_text)? | object_keys | populate_record(?:set)? | - typeof | to_record(?:set)? | strip_nulls - ) | - jsonb_(?>set|insert|pretty|path_exists|path_match|path_query(?>_array|_first)?) - ) | + # Scalar values. + current_(?>catalog|role|schema) | localtime(?:stamp)? + ) + | ( # Should always be keywords, but SQL.sublime-syntax doesn't think so yet. - ( - any|all|array|cast|cross|column|collat(?:e|ion)|create|distinct|except| - fetch|full|group|intersect|into|inner|isnull|i[sn]|ilike| - left|not|on|offset|outer|over|table|window - ) | - (?:(nulls) \s+ (last)) | - # Some common (Postgres) functions. - ( - now | coalesce | nullif | greatest | least | - (?>array|string|jsonb?)_agg | random | row_number | - to_ts(?>query|vector) | setweight | replace + # Taken from https://www.postgresql.org/docs/current/sql-keywords-appendix.html + all|any|array|analy[sz]e|a?symmetric|authorization|at(?=\s+time\s+zone\b)|binary|both|by + | (?<=\bat\s)time(?=\s+zone\b) | (?<=\btime\s)zone | (?<=\bdo\s)nothing | (?<=\bon\s)conflict | (?<=\bwith\s)ordinality + | cast|cross|column|concurrently|collat(?:e|ion)|create|distinct|(?>|[%^@|&#~]) | + | (::|\|?\|/|!!?|<<|>>|[%^@|&#~]) # Postgres type cast identifier. - (?<=::)\s*((?!\d)\w+) | + | (?<=::)\s*((?!\d)\w+(?:\[\])*) # Postgres jsonb operators. - (\\\\)(\?[|&]?)? | (->>?|\#>>?|@[@>?]|<@|\#-) | + | (\\\\)(\?[|&]?)? | (->>?|\#>>?|@[@>?]|<@|\#-) # Ecto argument placeholder. - ((?