diff --git a/Elixir/Elixir (EEx).sublime-syntax b/Elixir/Elixir (EEx).sublime-syntax new file mode 100644 index 0000000000..42de4b822e --- /dev/null +++ b/Elixir/Elixir (EEx).sublime-syntax @@ -0,0 +1,61 @@ +%YAML 1.2 +--- +# TODO: uncomment when on separate ST4 branch in the future. +# version: 2 +name: Elixir (EEx) +file_extensions: + - ex.eex + - exs.eex +first_line_match: ^#\s*exs?\.eex +scope: source.ex.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 diff --git a/Elixir/Elixir.sublime-syntax b/Elixir/Elixir.sublime-syntax new file mode 100644 index 0000000000..79b44e3ba6 --- /dev/null +++ b/Elixir/Elixir.sublime-syntax @@ -0,0 +1,2112 @@ +%YAML 1.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 + +variables: + module_name: '[A-Z][a-zA-Z\d_]*\b' + atom_id_suffix: '[\w@]*[?!]?' + atom_id: (?>[[:alpha:]_]{{atom_id_suffix}}) + identifier: (?>[[:lower:]_]\w*[?!]?) + not_id_key_suffix: \b(?!{{atom_id_suffix}}:(?!:)|[?!]) + + # 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}}) + +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 + + 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 + + fn_block: + - match: fn{{not_id_key_suffix}} + scope: keyword.declaration.function.elixir punctuation.section.block.begin.elixir + 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 + + function_header_pop: + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_closing_pop + - match: '' + push: function_header_args_pop + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_ws_closing_pop + - match: '' + push: function_header_args_pop + + function_header_args_pop: + - match: unquote(?=\s*\() + scope: keyword.other.elixir + set: [function_params_or_eol_pop, arguments_pop] + + - include: do_block_pop + - include: elixir_keywords + + - match: '{{identifier}}(?=\s){{is_argument_token}}' + comment: free-form header, e.g. "def func a, b do {a, b} end" + 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 + + - match: '{{identifier}}(?=\s*([(),;]|do{{not_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 + + - match: (?=\S) + set: + - match: | + (?x) + (?>(_{{identifier}}?)|({{identifier}}))? + \s* + ({{operators}}|(?>in|and|or){{not_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 + - include: function_params_or_eol_pop + + function_ws_params_pop: + - include: do_block_pop + - include: if_closing_tokens_pop + - include: parens + - include: parameters + - include: if_non_space_pop + + function_params_or_eol_pop: + - match: \( + scope: punctuation.definition.parameters.begin.elixir + set: + - meta_scope: meta.function.parameters.elixir + - match: \) + scope: punctuation.definition.parameters.end.elixir + set: function_post_params_pop + - include: parameters + - match: (?=,) + set: function_post_params_pop + - include: do_block_pop + - include: if_closing_tokens_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 + + ## Parameters + + fn_parameters: + # NB: no default parameters in fn blocks + - match: \\\\(?!:) + scope: keyword.operator.other.elixir invalid.illegal.default-operator.elixir + - include: parameters + + parameters: + - include: block_or_keyword + - match: __MODULE__{{not_id_key_suffix}} + scope: variable.language.special-form.elixir + - match: | + (?x) + (?>((?[,;)}\]=|\\]|\.\.|::|->|(?>when|in|do){{not_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 + push: + - match: (?=[,)]|(?>when|do){{not_id_key_suffix}}) + pop: true + - include: core_syntax + - match: (?=\() + set: paren_param_pop + - include: tuple_param + - include: list_param + - include: map_param + - include: binary_string_param + - include: core_syntax + + paren_param_pop: + - match: \( + scope: punctuation.definition.parameters.begin.elixir + set: + - meta_scope: meta.function.parameters.elixir + - match: \) + scope: punctuation.definition.parameters.end.elixir + pop: true + - include: comma_and_skip_ws + - include: parameters + + tuple_param: + - match: \{ + scope: punctuation.section.sequence.begin.elixir + push: + - meta_scope: meta.sequence.tuple.elixir + - match: \} + scope: punctuation.section.sequence.end.elixir + pop: true + - include: comma_and_skip_ws + - include: parameters + + list_param: + - match: \[ + scope: punctuation.section.brackets.begin.elixir + push: + - meta_scope: meta.brackets.elixir + - match: \] + scope: punctuation.section.brackets.end.elixir + pop: true + - include: comma_and_skip_ws + - include: parameters + - include: cons_operator + + map_param: + - match: \% + scope: punctuation.section.mapping.begin.elixir + push: + - match: _(?:{{module_name}}|{{identifier}})? + scope: variable.other.unused.elixir + set: map_param_body_pop + - include: alias_names + - 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: if_non_space_or_eol_pop + + binary_string_param: + - include: stray_binary_end + - match: <<(?![<~]) + scope: string.other.binary.elixir punctuation.definition.string.begin.elixir + push: + - meta_scope: meta.string.binary.elixir + - include: binary_string_body_pop + - include: parameters + + ## Types + + spec_definition_pop: + - match: (?=\() + set: [type_definition_next_pop, type_arguments_or_pop] + - include: type_definition_next_pop + + 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 + set: spec_op_definition_next_pop + - include: spec_op_definition_next_pop + + spec_op_definition_next_pop: + - include: return_types_pop + - include: types + - include: if_non_space_pop + + type_definition_pop: + - match: (?=\() + set: [type_definition_next_pop, paren_param_pop] + - include: type_definition_next_pop + + type_definition_next_pop: + - include: comments + - include: return_types_pop + - include: if_non_space_pop + + types: + - include: comments + - include: unquote_call + - include: block_or_keyword + - match: __MODULE__{{not_id_key_suffix}} + scope: variable.language.special-form.elixir + - 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 + push: type_arguments_or_pop + - include: paren_type + - include: tuple_type + - include: list_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: core_syntax + + dot_type: + - match: \.(?!\.) + scope: punctuation.accessor.dot.elixir + push: + - match: '{{identifier}}' + scope: storage.type.remote.elixir + set: type_arguments_or_pop + - include: if_non_space_pop + + type_arguments_or_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: named_type + - include: types + - include: union_operator + - include: empty_pop + + named_type: + - match: '{{identifier}}(?=\s*::(?!:))' + scope: variable.other.named-type.elixir + + return_types_pop: + - match: ::(?!:) + scope: 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}}) + comment: no when clause directly after "::" + pop: true + - match: '' + set: + - include: named_type + - match: (?=\|(?![:|>])|::(?!:)|when{{not_id_key_suffix}}) + set: next_types_pop + # NB: we can't prevent matching more than one type expression. + - include: types + - include: if_eol_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: (\|)|(::(?!:)) + 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_non_space_or_eol_pop + + union_types_eol_pop: + - include: named_type + - include: types + - include: if_non_space_or_eol_pop + + when_types_clause_pop: + - include: comma_and_skip_ws + - include: comments + - include: atom_keyword + - match: (?<=[\w"'@]:) + comment: skips to keyword value + push: [when_var_pop, if_non_space_pop] + - include: if_non_space_or_eol_pop + + when_var_pop: + - match: var{{not_id_key_suffix}} + 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 + push: + - meta_scope: meta.parens.elixir + - match: \) + scope: punctuation.definition.parens.end.elixir + pop: true + - include: comma_and_skip_ws + - include: named_type + - include: types + - include: union_operator + + tuple_type: + - match: \{ + scope: punctuation.section.sequence.begin.elixir + push: + - meta_scope: meta.braces.elixir + - match: \} + scope: punctuation.section.sequence.end.elixir + pop: true + - include: comma_and_skip_ws + - include: named_type + - include: types + - include: union_operator + + list_type: + - match: \[ + scope: punctuation.section.brackets.begin.elixir + push: + - meta_scope: meta.brackets.elixir + - match: \] + scope: punctuation.section.brackets.end.elixir + pop: true + - include: comma_and_skip_ws + - include: named_type + - include: types + - include: union_operator + + map_type: + - match: \% + scope: punctuation.section.mapping.begin.elixir + push: + - include: alias_names + - match: _(?:{{module_name}}|{{identifier}})? + scope: variable.type.unused.elixir invalid.illegal.struct.elixir + set: map_type_body_pop + - include: map_type_body_pop + + map_type_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: named_type + - include: types + - include: union_operator + - include: if_non_space_or_eol_pop + + binary_string_type: + - include: stray_binary_end + - match: <<(?![<~]) + scope: string.other.binary.elixir punctuation.definition.string.begin.elixir + push: + - meta_scope: meta.string.binary.elixir + - include: binary_string_body_pop + - include: named_type + - include: types + - include: union_operator + + numeric: + - match: | + (?x) + ( + 0x\h[\h_]* | + 0b[01](?:_?[01])* | + 0o[0-7](?:_?[0-7])* | + \d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][-+]?\d(?:_?\d)*)? + )(\w*) + comment: (?:_?\d)* because double or trailing '_' are invalid + captures: + 1: constant.numeric.elixir + 2: invalid.illegal.numeric.elixir + + binary_string: + - include: stray_binary_end + - match: <<(?![<~]) + scope: string.other.binary.elixir punctuation.definition.string.begin.elixir + push: + - meta_scope: meta.string.binary.elixir + - include: binary_string_body_pop + - include: core_syntax + + stray_binary_end: + - match: '>>(?!>)' + scope: punctuation.definition.string.end.elixir invalid.illegal.stray-closing-binary.elixir + + binary_string_body_pop: + - 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: if_non_space_pop + + escaped_char: + - match: \\x\h{2} + scope: constant.character.escape.hex.elixir + # Avoid possibly matching closing string delimiter: [^'"/)\]}>] + - match: \\x(?:\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}}) + scope: constant.character.escape.unicode.elixir + - match: \\u(?:{}|{?[^'"/)\]}>]{1,6}}?) + scope: invalid.illegal.escape.unicode.elixir + - match: \\.|\\\n + 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 + set: + - clear_scopes: true + - meta_content_scope: source.elixir.embedded + - meta_scope: source.elixir meta.string.elixir meta.interpolation.elixir + - match: \} + scope: punctuation.section.interpolation.end.elixir + pop: true + - include: core_syntax + + escaped_or_interpolated: + - include: escaped_char + - include: interpolated_elixir + + regex_elixir: + - include: scope:source.regexp.elixir + + regex_or_interpolated: + - include: interpolated_elixir + - include: regex_elixir + + + simple_string: + - match: \' + comment: single quoted string (allows for interpolation) + scope: punctuation.definition.string.begin.elixir + push: + - meta_scope: meta.string.elixir string.quoted.single.elixir + - match: \' + scope: punctuation.definition.string.end.elixir + pop: true + - include: escaped_or_interpolated + + - match: \" + comment: double quoted string (allows for interpolation) + scope: punctuation.definition.string.begin.elixir + push: + - meta_scope: meta.string.elixir string.quoted.double.elixir + - match: \" + scope: punctuation.definition.string.end.elixir + pop: true + - include: escaped_or_interpolated + + heredoc_regex_interpolated: + - match: (""")(.*)\n + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - match: (''')(.*)\n + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir string.quoted.triple.single.elixir + - include: heredoc_string_closing_single_pop + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - match: (?=^(?>\\.|[^'])*?''') + pop: true + + heredoc_regex_raw: + - match: (""")(.*)\n + comment: Triple-quoted heredocs + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: regex_elixir + with_prototype: + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - match: (''')(.*)\n + comment: Triple-quoted heredocs + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir string.quoted.triple.single.elixir + - include: heredoc_string_closing_single_pop + - match: '' + push: regex_elixir + with_prototype: + - match: (?=^(?>\\.|[^'])*?''') + pop: true + + heredoc_string_interpolated: + - match: (""")(.*)\n + comment: Triple-quoted heredocs + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: escaped_or_interpolated + - include: heredoc_string_closing_double_pop + + - match: (''')(.*)\n + comment: Triple-quoted heredocs + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir string.quoted.triple.single.elixir + - include: escaped_or_interpolated + - include: heredoc_string_closing_single_pop + + heredoc_string_raw: + - match: (""")(.*)\n + comment: Triple-quoted heredocs + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + 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 + captures: + 1: punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + 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*(""") + captures: + 1: invalid.illegal.closing-heredoc.elixir + 2: punctuation.definition.string.end.elixir + pop: true + + heredoc_string_closing_single_pop: + - match: ^\s*((?>\\.|[^'])*?)\s*(''') + captures: + 1: invalid.illegal.closing-heredoc.elixir + 2: punctuation.definition.string.end.elixir + pop: true + + string: + - include: heredoc_string_interpolated + - include: simple_string + - include: binary_string + + - match: (?x) (~[a-zA-Z])\n | ~[a-zA-Z]([^{\[<(/|"']) + comment: catch invalid sigils first + scope: meta.string.elixir storage.type.string.elixir + captures: + 1: invalid.illegal.sigil-string.elixir + 2: invalid.illegal.string-delimiter.elixir + + # Look for 'a' behind the closing delimiter. + # Bracket delimiters are not matched yet: <>, {}, [] and () + - match: (?=~w([/|"'])(?>\\.|(?!\1).)*\1a) + comment: highlight words as atoms + push: + - match: (~w)(.) + captures: + 1: storage.type.string.elixir + 2: string.quoted.other.atom.elixir punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir + - meta_content_scope: string.quoted.other.atom.elixir constant.other.symbol.atom.elixir + - match: \s+ + push: + - clear_scopes: 1 + - include: empty_pop + - include: escaped_or_interpolated + - match: (\2)(a) + captures: + 1: string.quoted.other.atom.elixir punctuation.definition.string.end.elixir + 2: string.quoted.modifiers.elixir storage.type.string.elixir + pop: true + + - 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 + 2: invalid.illegal.opening-heredoc.elixir + set: + - meta_scope: meta.string.elixir + - match: (?=^(?>\\.|[^"])*?""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: scope:text.html.eex + with_prototype: + - match: \\" + scope: constant.character.escape.char.elixir + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - 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 + 2: invalid.illegal.opening-heredoc.elixir + set: + - meta_scope: meta.string.elixir + - match: (?=^(?>\\.|[^"])*?""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: scope:source.yaml + with_prototype: + - include: escaped_or_interpolated + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - 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 + 2: invalid.illegal.opening-heredoc.elixir + set: + - meta_scope: meta.string.elixir + - match: (?=^(?>\\.|[^"])*?""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: scope:source.yaml + with_prototype: + - match: \\" + scope: constant.character.escape.char.elixir + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - 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 + 2: invalid.illegal.opening-heredoc.elixir + set: + - meta_scope: meta.string.elixir + - match: (?=^(?>\\.|[^"])*?""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: scope:source.json + with_prototype: + - include: interpolated_elixir + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - 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 + 2: invalid.illegal.opening-heredoc.elixir + set: + - meta_scope: meta.string.elixir + - match: (?=^(?>\\.|[^"])*?""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - match: '' + push: scope:source.json + with_prototype: + - match: \\" + scope: constant.character.escape.char.elixir + - match: (?=^(?>\\.|[^"])*?""") + pop: true + + - match: ~r + comment: regex sigil string with interpolation + scope: meta.string.elixir storage.type.string.elixir + push: + - match: (?="""|''') + set: + - match: (?<="""|''') + set: string_modifiers_and_pop + - include: heredoc_regex_interpolated + - match: \" + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - match: \" + set: string_modifiers_and_pop + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - 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 + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - 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 + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - 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 + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - match: (?=\|) + pop: true + - match: \{ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_curly + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - match: (?=}) + pop: true + - match: \[ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_square + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - match: (?=]) + pop: true + - match: \< + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_angle + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - match: (?=>) + pop: true + - match: \( + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_round + - match: '' + push: regex_elixir + with_prototype: + - include: interpolated_elixir + - include: if_closing_paren_pop + + - match: ~R + comment: regex sigil string without interpolation + scope: meta.string.elixir storage.type.string.elixir + push: + - match: (?="""|''') + set: + - match: (?<="""|''') + set: string_modifiers_and_pop + - include: heredoc_regex_raw + # NB: not used as it doesn't correctly match the closing / inside the char set: ~R/[/]/ + # - match: (?=[/|"']) + # set: + # - match: (?<=[/|"']) + # set: string_modifiers_and_pop + # - match: ([/|"']) + # scope: punctuation.definition.string.begin.elixir + # push: regex_elixir + # with_prototype: + # - match: \1 + # scope: punctuation.definition.string.end.elixir + # 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 + - match: '' + push: regex_elixir + with_prototype: + - 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 + - match: '' + push: regex_elixir + with_prototype: + - 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 + - match: '' + push: regex_elixir + with_prototype: + - 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 + - match: '' + push: regex_elixir + with_prototype: + - match: (?=\|) + pop: true + - match: \{ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir + - include: string_closing_curly + - match: '' + push: regex_elixir + with_prototype: + - match: (?=}) + pop: true + - match: \[ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir + - include: string_closing_square + - match: '' + push: regex_elixir + with_prototype: + - match: (?=]) + pop: true + - match: \< + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir + - include: string_closing_angle + - match: '' + push: regex_elixir + with_prototype: + - match: (?=>) + pop: true + - match: \( + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.regex.elixir + - include: string_closing_round + - match: '' + push: regex_elixir + with_prototype: + - include: if_closing_paren_pop + + - match: ~[a-z] + comment: with sigil and with interpolation + scope: meta.string.elixir storage.type.string.elixir + push: + - match: (?="""|''') + set: + - match: (?<="""|''') + set: string_modifiers_and_pop + - include: heredoc_string_interpolated + - match: (?=[/|"']) + set: + - meta_scope: meta.string.elixir + # (?<=[a-z]) avoids matching again after the closing delimiter. E.g.: ~s||// + - match: (?<=[a-z])([/|"']) + captures: + 1: string.quoted.other.literal.lower.elixir punctuation.definition.string.begin.elixir + push: + - meta_content_scope: string.quoted.other.literal.lower.elixir + - match: \1 + scope: string.quoted.other.literal.lower.elixir punctuation.definition.string.end.elixir + pop: true + - include: escaped_or_interpolated + - match: '' + set: string_modifiers_and_pop + - match: \{ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_curly + - include: escaped_or_interpolated + - match: \[ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_square + - include: escaped_or_interpolated + - match: \< + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_angle + - include: escaped_or_interpolated + - match: \( + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.interpolated.elixir + - include: string_closing_round + - include: escaped_or_interpolated + + - match: ~[A-Z] + comment: with sigil and without interpolation + scope: meta.string.elixir storage.type.string.elixir + push: + - match: (?="""|''') + set: + - match: (?<="""|''') + set: string_modifiers_and_pop + - include: heredoc_string_raw + - match: (?=[/|"']) + set: + - meta_scope: meta.string.elixir + # (?<=[A-Z]) avoids matching again after the closing delimiter. E.g.: ~S||// + - match: (?<=[A-Z])([/|"']) + captures: + 1: string.quoted.other.literal.upper.elixir punctuation.definition.string.begin.elixir + push: + - meta_content_scope: string.quoted.other.literal.upper.elixir + - 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: \{ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.upper.elixir + - include: string_closing_curly + - match: \[ + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.upper.elixir + - include: string_closing_square + - match: \< + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.upper.elixir + - include: string_closing_angle + - match: \( + scope: punctuation.definition.string.begin.elixir + set: + - meta_scope: meta.string.elixir string.quoted.other.literal.upper.elixir + - include: string_closing_round + + string_closing_curly: + - match: \\\} + scope: constant.character.escape.char.elixir + - match: \} + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_closing_square: + - match: \\\] + scope: constant.character.escape.char.elixir + - match: \] + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_closing_angle: + - match: \\\> + scope: constant.character.escape.char.elixir + - match: \> + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_closing_round: + - match: \\\) + scope: constant.character.escape.char.elixir + - match: \) + scope: punctuation.definition.string.end.elixir + set: string_modifiers_and_pop + + string_modifiers: + - match: '[a-zA-Z]+' + scope: meta.string.elixir string.quoted.modifiers.elixir storage.type.string.elixir + - match: \w+ + scope: invalid.illegal.non-ascii-modifier.elixir + + string_modifiers_and_pop: + - include: string_modifiers + - include: empty_pop + + def_blocks: + - 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 + + defmodule_body_pop: + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_closing_pop + - include: defmodule_arguments + - match: \s? + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_ws_closing_pop + - include: defmodule_arguments + + defmodule_arguments: + - include: block_or_keyword + - match: (?={{module_name}}|__MODULE__{{not_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*\.)' + scope: entity.name.namespace.elixir + - include: special_form + - include: module_name + - include: atom_symbol + - include: module_accessor_or_pop + - include: arg_comma_and_skip_ws + - include: core_syntax + + do_block_pop: + - match: do{{not_id_key_suffix}} + scope: punctuation.section.block.begin.elixir keyword.context.block.elixir + set: + - meta_scope: meta.block.elixir + - include: block_end_pop + - include: core_syntax + + do_block: + # NB: Commented out due to EEx templates: <%= if x do %>...<% end %> + # - match: end{{not_id_key_suffix}} + # scope: invalid.illegal.block-end.elixir + - match: (?=do{{not_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 + pop: true + + module_name: + - match: '{{module_name}}{{not_id_key_suffix}}' + scope: constant.other.module.elixir + + 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 + + module_accessor_or_pop: + - match: \.(?!\.) + scope: punctuation.accessor.elixir + - include: if_non_space_or_eol_pop + + module_function_call_pop: + - match: ({{module_name}})\s*(\.(?!\.))\s*(?={{identifier}}|{{operators}}) + 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 + + identifier_operator_call_pop: + - match: ({{identifier}}|{{operators}})(?=\s*\.\s*\(|{{has_arguments}}) + scope: variable.function.elixir + set: arguments_paren_or_ws_pop + + quoted_remote_call_pop: + - match: \" + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: meta.function-call.elixir + - match: \" + scope: punctuation.definition.constant.end.elixir + set: arguments_or_pop + - match: (\\[\\"])|[^"] + scope: variable.function.elixir + captures: + 1: constant.character.escape.char.elixir + - match: \' + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: meta.function-call.elixir + - match: \' + scope: punctuation.definition.constant.end.elixir + set: arguments_or_pop + - match: (\\[\\'])|[^'] + scope: variable.function.elixir + captures: + 1: constant.character.escape.char.elixir + + modules_or_identifiers_or_calls: + - 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 + pop: true + + 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: module_function_call_pop + - include: identifier_operator_call_pop + - include: quoted_remote_call_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 + + module_attribute: + - match: '@' + scope: keyword.operator.attribute.elixir + push: module_attribute_pop + + module_attribute_pop: + - match: (?!{{identifier}}(?!{{atom_id_suffix}}:)) + set: + - match: '[A-Z]' + scope: invalid.illegal.attribute.elixir + - include: 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: (?>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}}) + scope: keyword.declaration.type.elixir + set: + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - include: arguments_closing_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 + + - match: (?>typep?|opaque){{no_suffix_then_arguments}} + scope: keyword.declaration.type.elixir + set: + # TODO: + # - meta_content_scope: meta.type.elixir + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - include: arguments_closing_pop + - include: block_or_keyword + - match: unquote(?=\s*\() + scope: keyword.other.elixir + push: [type_definition_pop, arguments_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: 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 + - 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_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 + set: spec_definition_pop + + markdown_comment: + - match: (""")(.*)\n + captures: + 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir + - match: (?=^\s*""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - include: escaped_or_interpolated + - 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}})|\\|#{) + + - match: \" + scope: string.quoted.double.elixir punctuation.definition.string.begin.elixir + push: + - meta_scope: meta.string.elixir + - match: \" + scope: string.quoted.double.elixir punctuation.definition.string.end.elixir + pop: true + - include: escaped_or_interpolated + - match: '' + embed: scope:text.html.markdown + embed_scope: text.html.eex source.elixir.embedded.html + escape: (?="|\\|#{) + + - match: ~S(""")(.*)\n + scope: meta.string.elixir storage.type.string.elixir + captures: + 1: string.quoted.triple.double.elixir punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir + - match: (?=^\s*""") + set: + - meta_scope: meta.string.elixir string.quoted.triple.double.elixir + - include: heredoc_string_closing_double_pop + - 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}})) + + - match: ~S(''')(.*)\n + scope: meta.string.elixir storage.type.string.elixir + captures: + 1: string.quoted.triple.single.elixir punctuation.definition.string.begin.elixir + 2: invalid.illegal.opening-heredoc.elixir + push: + - meta_scope: meta.string.elixir + - match: (?=^\s*''') + set: + - meta_scope: meta.string.elixir string.quoted.triple.single.elixir + - include: heredoc_string_closing_single_pop + - 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}})) + + 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).*?(>) + captures: + 1: keyword.other.iex.elixir + 2: 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 + push: + - clear_scopes: 2 + - meta_scope: meta.interpolation.elixir markup.raw.block.markdown markup.raw.block.elixir + - match: '```\n?' + scope: punctuation.definition.code-block.end.markdown + 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 + + comma_and_skip_ws: + - match: \, + scope: punctuation.separator.sequence.elixir + push: invalid_comma_or_non_space_pop + + arg_comma_and_skip_ws: + - match: \, + scope: punctuation.separator.arguments.elixir + 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 + + 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 + + # Separate to make @type matching possible. + cons_operator: + - match: \| + scope: keyword.operator.cons.elixir + + tuple: + - match: \} + scope: invalid.illegal.stray-closing-brace.elixir + - match: \{ + scope: punctuation.section.sequence.begin.elixir + push: + - meta_scope: meta.sequence.tuple.elixir + - match: \} + scope: punctuation.section.sequence.end.elixir + pop: true + - include: comma_and_skip_ws + - include: core_syntax + + list: + - match: \] + scope: invalid.illegal.stray-closing-bracket.elixir + - match: \[ + scope: punctuation.section.brackets.begin.elixir + push: + - meta_scope: meta.brackets.elixir + - match: \] + scope: punctuation.section.brackets.end.elixir + pop: true + - include: comma_and_skip_ws + - include: core_syntax + - include: cons_operator + + map: + - match: \% + scope: punctuation.section.mapping.begin.elixir + push: + - include: alias_names + - match: _(?:{{module_name}}|{{identifier}})? + scope: variable.other.unused.elixir + set: map_body_pop + - include: map_body_pop + + map_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: core_syntax + - include: cons_operator + - include: if_non_space_or_eol_pop + + parens: + - match: \) + scope: invalid.illegal.stray-closing-parenthesis.elixir + - match: \( + scope: punctuation.section.parens.begin.elixir + push: + - meta_scope: meta.parens.elixir + - match: \) + scope: punctuation.section.parens.end.elixir + pop: true + - include: core_syntax + + alias: + - match: (?>alias|require){{no_suffix_then_arguments}} + scope: keyword.control.import.elixir + push: + # alias(X, as: Y) + - match: \( + scope: punctuation.section.arguments.begin.elixir + set: + - meta_scope: meta.function-call.arguments.elixir + - include: arguments_closing_pop + - include: alias_names + - match: (?=,) + set: + - include: alias_as_arg + - include: arg_comma_and_skip_ws + - include: arguments_closing_pop + - include: core_syntax + - include: core_syntax + - 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 + - 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] + + 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 + + alias_names: + - match: (?=(?>{{module_name}}|__MODULE__){{not_id_key_suffix}}|:(?>{{atom_id}}|['"])) + 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: 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 + + 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 + + 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}}(:)(?!:) + 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:(?!:)) + comment: keyword string + push: + - match: (.) + scope: punctuation.definition.constant.begin.elixir + set: + - meta_scope: meta.string.elixir constant.other.keyword.elixir + - include: escaped_or_interpolated + - match: '\1:' + scope: punctuation.definition.constant.end.elixir + 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 + push: + - meta_scope: constant.other.symbol.double-quoted.elixir + - match: \" + scope: punctuation.definition.constant.end.elixir + pop: true + - include: escaped_or_interpolated + + capture: + - match: \&(?=\s*(?>{{module_name}}\s*\.(?!\.)\s*)*(?>{{identifier}}|{{operators}})\s*/\s*\d) + scope: keyword.operator.capture.elixir + push: + - match: (/)\s*(\d+) + captures: + 1: punctuation.accessor.slash.elixir + 2: constant.numeric.integer.decimal.elixir + pop: true + - include: module_names + - match: '{{identifier}}|{{operators}}' + scope: variable.other.capture.elixir + - match: (\&0)|(\&\d+)|(\&) + captures: + 1: invalid.illegal.capture.elixir + 2: constant.other.capture.elixir + 3: keyword.operator.capture.elixir + + sql_fragment: + - match: (fragment)\s*(\() + captures: + 1: support.function.elixir + 2: 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 + + sql_string_pop: + - match: '"""\n' + scope: string.quoted.double.elixir punctuation.definition.string.begin.elixir + set: + - match: '"""' + scope: string.quoted.double.elixir punctuation.definition.string.end.elixir + pop: true + - match: '' + push: scope:source.ex.sql + with_prototype: + - match: (?=""") + pop: true + - include: sql_or_elixir_escaped_pop + - match: \" + scope: string.quoted.double.elixir punctuation.definition.string.begin.elixir + set: + - match: \" + scope: string.quoted.double.elixir punctuation.definition.string.end.elixir + pop: true + - match: '' + push: scope:source.ex.sql + with_prototype: + - match: (?=") + pop: true + - include: sql_or_elixir_escaped_pop + + sql_or_elixir_escaped_pop: + - match: (?=\\(?!\\?\?)) + # NB: using push instead of set here breaks highlighting for ST3. + set: + - meta_scope: source.ex.sql + - match: (?=\\\\?\?) + comment: let source.ex.sql match "\\?" + pop: true + - include: escaped_char + - include: empty_pop + + sql_fragment_args_pop: + - include: arguments_closing_pop + - include: arg_comma_and_skip_ws + - 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 + + comments: + - match: (?=#) + push: + - include: comment + - include: if_non_space_pop + + char_literal: + - match: \?$ + scope: invalid.illegal.character-literal.elixir + - match: \?(?>\\(?>.|\n)|\S|(\s)) + comment: character literal as an integer + scope: constant.numeric.elixir + captures: + 1: 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 + ){{no_suffix_then_arguments}} + scope: variable.function.built-in.elixir + push: arguments_paren_or_ws_pop + + + arguments_paren_or_ws_pop: + - include: arguments_pop + - match: '' + set: [arguments_ws_closing_pop, arguments_ws_pop] + + arguments_or_pop: + - match: '{{has_arguments}}' + set: arguments_paren_or_ws_pop + - include: 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 + + 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 + pop: true + - match: (?<=::\bend\b)|(?<=([^:])\bend\b)|(?<=^end\b) + scope: punctuation.section.arguments.end.elixir + pop: true + + 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 + + special_form: + - match: __(?>MODULE|ENV|DIR|CALLER|STACKTRACE)__{{not_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(?![?!]) + comment: fully reserved keywords + scope: keyword.other.elixir + - match: (?>nil|true|false)\b(?![?!]) + scope: constant.language.elixir + + 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 + push: arguments_paren_or_ws_pop + + unquote_call: + - match: unquote(?=\s*\() + scope: keyword.other.elixir + push: arguments_paren_or_ws_pop + + spaces_pop: + - match: \s* + pop: true + + if_closing_tokens_no_eol_pop: + - match: (?=\s*(?>[})\];]|%>|{{closing_keywords}})) + pop: true + + if_closing_tokens_pop: + - include: if_closing_tokens_no_eol_pop + - include: if_eol_pop + + if_closing_paren_pop: + - match: (?=\)) + pop: true + + if_non_space_pop: + - match: (?=\S) + pop: true + + if_non_space_or_eol_pop: + - match: (?=\S|$) + pop: true + + if_eol_pop: + - match: $ + pop: true + + empty_pop: + - match: '' + pop: true diff --git a/Elixir/HTML (EEx).sublime-syntax b/Elixir/HTML (EEx).sublime-syntax new file mode 100644 index 0000000000..3070d8b990 --- /dev/null +++ b/Elixir/HTML (EEx).sublime-syntax @@ -0,0 +1,51 @@ +%YAML 1.2 +--- +name: HTML (EEx) +file_extensions: + - html.eex + - html.leex +scope: text.html.eex + +contexts: + main: + - match: '' + push: scope:text.html.basic + with_prototype: + - include: eex_tags + + eex_tags: + - match: <%# + push: + - meta_scope: comment.eex + - match: '%>' + 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 + pop: true + - match: <%(?>%=?|[=/|]?) + scope: text.html.eex keyword.other.eex punctuation.section.embedded.begin.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 "...": + # Apparent recursion within a with_prototype action: 25000 context sanity limit hit + # ``` + # The error is caused by all the syntax embedding within Elixir.sublime-syntax. + # This would've solved highlighting certain files in Phoenix correctly, e.g.: + # 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 + # push: scope:source.eex + # with_prototype: + # - match: (?=%>) + # pop: true + # - include: eex_tags diff --git a/Elixir/Regular Expressions (Elixir).sublime-syntax b/Elixir/Regular Expressions (Elixir).sublime-syntax new file mode 100644 index 0000000000..eab88eeed6 --- /dev/null +++ b/Elixir/Regular Expressions (Elixir).sublime-syntax @@ -0,0 +1,480 @@ +%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/SOURCE b/Elixir/SOURCE new file mode 100644 index 0000000000..d1ef17a45c --- /dev/null +++ b/Elixir/SOURCE @@ -0,0 +1 @@ +https://github.com/princemaple/elixir-sublime-syntax diff --git a/Elixir/SQL (Elixir).sublime-syntax b/Elixir/SQL (Elixir).sublime-syntax new file mode 100644 index 0000000000..61ce352c23 --- /dev/null +++ b/Elixir/SQL (Elixir).sublime-syntax @@ -0,0 +1,62 @@ +%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)?) + ) | + # 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 + ) + )\b | + # Various Postgres operators. + (::|\|?\|/|!!?|<<|>>|[%^@|&#~]) | + # Postgres type cast identifier. + (?<=::)\s*((?!\d)\w+) | + # Postgres jsonb operators. + (\\\\)(\?[|&]?)? | (->>?|\#>>?|@[@>?]|<@|\#-) | + # Ecto argument placeholder. + ((?