├── .github └── workflows │ └── main.yml ├── .gitignore ├── BACKLOG.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── builds ├── Elixir - elixir FILE.sublime-build ├── Elixir - elixirc $file.sublime-build ├── Elixir - mix compile.sublime-build ├── Elixir - mix format FILE.sublime-build ├── Elixir - mix test FILE.sublime-build └── Elixir - mix test.sublime-build ├── color-schemes ├── Mariana.sublime-color-scheme └── Monokai.sublime-color-scheme ├── commands ├── Default.sublime-commands ├── __init__.py ├── hex_packages.py ├── mix_format.py ├── mix_test.py └── utils.py ├── completions ├── Phoenix_Attributes.sublime-completions └── Surface_Attributes.sublime-completions ├── dependencies.json ├── images ├── elixir_fragment_example.svg ├── elixir_heex_example.svg ├── elixir_json_example.svg ├── elixir_liveview_example.svg ├── elixir_regex_example.svg ├── elixir_sql_example.svg ├── elixir_surface_example.svg ├── elixir_type_example.svg └── elixir_yaml_example.svg ├── keymaps └── Default.sublime-keymap ├── main.py ├── menus └── Main.sublime-menu ├── preferences ├── Attributes.tmPreferences ├── EEx_Comments.tmPreferences ├── Elixir_Comments.tmPreferences ├── Fold.tmPreferences ├── Indentation_Rules.tmPreferences └── Surface_Comments.tmPreferences ├── scripts ├── count_tokens_by_scope.py └── print_html.py ├── settings └── ElixirSyntax.sublime-settings ├── snippets ├── EEx_Code.sublime-snippet ├── EEx_Comment.sublime-snippet ├── EEx_Interpolation.sublime-snippet ├── EEx_Raw.sublime-snippet ├── Elixir_Interpolation.sublime-snippet ├── IEx.pry.sublime-snippet ├── IO_inspect.sublime-snippet ├── IO_inspect_label.sublime-snippet ├── Kernel.dbg.sublime-snippet ├── Kernel.tap&.sublime-snippet ├── Kernel.tap.sublime-snippet ├── Kernel.then&.sublime-snippet └── Kernel.then.sublime-snippet ├── syntaxes ├── EEx.sublime-syntax ├── Elixir (EEx).sublime-syntax ├── Elixir.sublime-syntax ├── HTML (EEx).sublime-syntax ├── HTML (HEEx).sublime-syntax ├── HTML (Surface).sublime-syntax ├── PCRE (Erlang).sublime-syntax └── SQL (Elixir).sublime-syntax └── tests ├── syntax_test_atoms.ex ├── syntax_test_attributes.ex ├── syntax_test_declarations.ex ├── syntax_test_doc.ex ├── syntax_test_function_calls.ex ├── syntax_test_misc.ex ├── syntax_test_numerics.ex ├── syntax_test_operators.ex ├── syntax_test_regexp.ex ├── syntax_test_sql.ex.sql ├── syntax_test_sql_fragments.ex ├── syntax_test_strings.ex ├── syntax_test_surface.ex ├── syntax_test_template.eex ├── syntax_test_template.ex.eex ├── syntax_test_template.html.eex ├── syntax_test_template.html.heex ├── syntax_test_template.sface ├── syntax_test_types.ex └── syntax_test_variables.ex /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Syntax Tests 2 | 3 | on: 4 | push: 5 | paths: 6 | - '.github/workflows/main.yml' 7 | - '**.sublime-syntax' 8 | - '**/syntax_test*' 9 | - '**.tmPreferences' 10 | pull_request: 11 | paths: 12 | - '.github/workflows/main.yml' 13 | - '**.sublime-syntax' 14 | - '**/syntax_test*' 15 | - '**.tmPreferences' 16 | 17 | jobs: 18 | st4_syntax_tests: 19 | name: Run ST4 Syntax Tests 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: SublimeText/syntax-test-action@v2 24 | with: 25 | build: 4192 26 | default_packages: v4192 27 | package_name: ElixirSyntax 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-project 2 | *.sublime-workspace 3 | -------------------------------------------------------------------------------- /BACKLOG.md: -------------------------------------------------------------------------------- 1 | # Backlog 2 | 3 | The backlog contains tasks that are planned to be worked on or ideas that may be useful. 4 | 5 | * EEx/HEEx: fix matching EEx tags inside `script` and `style` tags, the CSS `style=""` and the JS `on...=""` attributes. 6 | * Elixir: match do-block after function call: `func(a, b) do end`. 7 | * Elixir: use ST4's branching feature to improve syntax matching rules. 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v4.1.0] – 2025-03-28 4 | 5 | - HEEx: support new syntax for embedding Elixir code with curly braces. 6 | 7 | ## [v4.0.0] – 2024-09-01 8 | 9 | - Elixir: improved matching of right-arrow clauses. 10 | - Elixir: recognize SQL strings inside `query("...")`, `query(Repo, "...")`, `query_many("...")`, `query_many(Repo, "...")` (including bang versions). 11 | - Elixir: fixed expressions in struct headers, e.g.: `%^module{}` and `%@module{}`. 12 | - Elixir: recognize all variants of atom word strings, e.g.: `~w"one two three"a` 13 | - Elixir: fixes to capture expressions: `& 1` is a capture with an integer, not the capture argument `&1`. `& &1.func/2`, `&var.member.func/3` and `&@module.func/1` are captured remote functions. 14 | - HEEx: recognize special attributes `:let`, `:for` and `:if`. 15 | - HEEx: fixed matching dynamic attributes, e.g.: `
`. 16 | - Commands: `mix_test` is better at finding the root `mix.exs` file and runs when the project hasn't been built yet. 17 | - Commands: `mix test` and `mix format` error locations can be double-clicked and jumped to. 18 | - Commands: read `mix` output unbuffered for immediate display in the output panel. 19 | - Commands: removed the `output_scroll_time` setting. The output will scroll automatically without delay. 20 | - Commands: run `mix test` with selected lines if no standard `test` blocks were found, allowing to run tests defined by macros such as `property/2`. 21 | - Commands: prevent executing `mix test` again if it's already running. 22 | - Completions: use double quotes instead of curly braces for `phx` attributes. 23 | 24 | ## [v3.2.3] – 2023-08-13 25 | 26 | - EEx, HEEx: use `<%!-- ... --%>` when toggling comments. 27 | - EEx, HEEx, Surface: highlight begin and end punctuation marks of comments. 28 | - Commands: fix: filter out already selected tests when using multi-cursor selections. 29 | 30 | ## [v3.2.2] – 2023-06-28 31 | 32 | - Elixir: fixed module function call regression in captures (`&Map.take(&1, @fields)`). 33 | - Elixir: recognize special macro `defmacro (..) do end`. 34 | - Commands: added `mix_test_hide_panel` command. 35 | 36 | ## [v3.2.1] – 2023-06-24 37 | 38 | - Elixir: fixed quoted module name function calls such as `:"Elixir.Kernel".in(1, [1])`. 39 | - SQL: recognize `CREATE TYPE`. 40 | 41 | ## [v3.2.0] – 2023-05-02 42 | 43 | - Commands: improved/generalized syntax detection for enabling/disabling commands. 44 | - Commands: fix: output both stdout/stderr when running `mix format`/`mix test`. 45 | - Commands: auto-scroll `mix format` output when it's compiling. 46 | - SQL: recognize `FILTER` in `array_agg(x) FILTER (...)`. 47 | 48 | ## [v3.1.5] – 2023-04-30 49 | 50 | - Elixir: recognize `name` in `defmodule name do end`. 51 | - Commands: fix: print `mix format` error output asynchronously. 52 | - Commands: fix: hide the `mix format` error panel when the command is successful again. 53 | 54 | ## [v3.1.4] – 2022-12-21 55 | 56 | - Commands: fix: call `mix format` asynchronously to avoid locking up the view. 57 | 58 | ## [v3.1.3] – 2022-12-15 59 | 60 | - Package: fix: added `dependencies.json` to require the `pathlib` library (#53). 61 | 62 | ## [v3.1.2] – 2022-12-13 63 | 64 | - Commands: recognize more file types to allow running `mix format` on. 65 | - Commands: mention possibly unsaved changes when a test wasn't found. 66 | 67 | ## [v3.1.1] – 2022-11-08 68 | 69 | - Commands: fixed `mix format` and `mix test` in non-project windows. 70 | - Commands: fixed finding counterpart of a test/code file in non-project windows. 71 | - Commands: ignore `.elixir_ls`, `_build` and `deps` folders when searching for the counterpart of a test/code file. 72 | 73 | ## [v3.1.0] – 2022-11-03 74 | 75 | - Commands: added `mix_test_show_panel`, `mix_test_switch_to_code_or_test`, `search_hex_packages` and `open_hex_docs`. 76 | + `Mix Test: Show Panel` reopens the test output panel if closed. 77 | + `Mix Test: Switch to Code or Test` jumps to the corresponding code file of a test and vice versa. 78 | + `ElixirSyntax: Open Hex Docs` displays a filterable list of all available projects on hexdocs.pm. 79 | + `ElixirSyntax: Search Hex Packages` searches for packages on hex.pm and displays the results in a list. 80 | - Palette: added `Mix Test: All`. 81 | - Palette: renamed caption `Mix Test: Set Seed` to `Mix Test: Set --seed`. 82 | 83 | ## [v3.0.0] – 2022-10-24 84 | 85 | - Elixir: removed Markdown highlighting from doc comments due to unfixable issues. 86 | - Elixir: properly highlight arrow clauses in `for`-statement arguments. 87 | - Elixir: match macro and record calls inside parameters and arrow clauses (`for module(module: module) <- all_modules`). 88 | - Elixir: fixed stepped ranges as parameters (`first..last//step`). 89 | - Elixir: fixed string interpolations clearing all scopes (`"2^8 = #{2 ** 8}"`). 90 | - Commands: added Python code to be able to call `mix test` in various ways. 91 | - Commands: added `mix_format_project` and `mix_format_file` commands with auto-format setting. 92 | - Palette: added `Mix Test: ...` and `Mix Format: ...` commands. 93 | - EEx: added syntax file for EEx in plain text files. 94 | - HTML (EEx), Elixir (EEx): added `<%!-- ... --%>` multi-line comments. 95 | - HTML (EEx): match EEx tags as tag and attribute names (`="value"/>`). 96 | - HTML (HEEx): fixed matching function names in tags. 97 | - HTML (HEEx): match phx binding attributes. 98 | - Elixir (EEx): fixed matching comments (`<%# ... %>`). 99 | - SQL: fixed matching decimal numbers. 100 | - SQL: fixed matching quoted member ids (`a_table."a column"`). 101 | - Snippets: added `dbg` keyword for `|> dbg()`. 102 | - Snippets: added EEx tags. 103 | - Snippets: added Elixir `#{...}` string interpolation. 104 | - Snippets: added `require IEx; IEx.pry()` string interpolation. 105 | - Completions: added Phoenix LiveView attribute bindings. 106 | - Completions: added Surface tag attributes. 107 | - Preferences: added increase / decrease indentation settings (thanks to @timfjord). 108 | - Builds: added `elixirc` and `mix compile`. 109 | - Menus: added "ElixirSyntax" to "Preferences > Package Settings". 110 | 111 | ## [v2.3.0] – 2021-12-17 112 | 113 | - Syntaxes: refactored Surface/HEEx/EEx with many improvements (thanks to @deathaxe). 114 | - Themes: slightly darken the embed punctuation markers for Surface and (H)EEx tags. 115 | - Elixir: allow digits in sigil string modifiers. 116 | - Preferences: index Elixir `@attribute` definitions for "Goto Definition". 117 | 118 | ## [v2.2.0] – 2021-09-18 119 | 120 | - Syntax: added support for the [HEEx](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Helpers.html#sigil_H/2) template syntax inside the `~H` sigil. 121 | - Syntax: added support for the [Surface](https://surface-ui.org/template_syntax) template syntax inside the `~F` sigil. 122 | - Elixir: match the `**` power operator. 123 | - HTML (EEx): switched to version 2 and removed usage of `with_prototype`. 124 | - SQL: match the `;` token; fixed the `/**/` comment scope. 125 | - Themes: highlight interpolated Elixir with a lighter background. 126 | - Themes: don't italicize the sigil type. 127 | 128 | ## [v2.1.0] – 2021-07-25 129 | 130 | - Elixir: disabled highlighting Elixir code within Markdown comments. Reasons: 131 | * Not completely reliable. 132 | * Not all Elixir code was recognized. 133 | * Can be distracting. 134 | * Probably affects speed. 135 | + If you'd like to re-enable this feature, override the package and uncomment the relevant lines. 136 | - Elixir: added `meta.type.elixir` scope to specs and types. 137 | - Elixir: added `meta.doc.elixir` scope to doc attributes. 138 | - Elixir: highlight an atom with a function call as a module constant: `:lists.sort([])` 139 | - Elixir: recognize a record's name as an entity symbol for "Goto Definition". 140 | - Elixir: recognize `record` as a special keyword in type declarations:\ 141 | `@type t :: record(:user, name: binary, age: integer)` 142 | - Elixir: fix: stop highlighting params after `when` in free-form functions: `def a + b when a == 0 and b == 0` 143 | - Elixir: fixed lambda calls in capture expressions: `&fun.(&1, &2)` 144 | - Elixir: consider a line continuation backslash to be the start of an argument list: 145 | ```elixir 146 | with \ 147 | {:ok, _} <- newline do 148 | end 149 | ``` 150 | - Elixir: allow `unquote` and `unquote_splicing` to have arguments without parentheses. 151 | - Elixir: match `.:` as an atom keyword. 152 | - Elixir: `^^` is not an operator. 153 | - Themes: don't italicize parameters in Monokai. 154 | - Themes: completely italicize types and specs, except for `::`, `|` and strings. 155 | - SQL: highlight `WITH ORDINALITY` and `AT TIME ZONE`. 156 | - Builds: removed `$` from the file names so they're correctly displayed in the menu. 157 | 158 | ## [v2.0.5] – 2021-05-30 159 | 160 | - Elixir: also highlight `catch`, `else`, `after` clauses in function do-end blocks. 161 | - Elixir: use own scopes for `after` and `rescue` keywords. 162 | - Elixir: allow commas inside item access brackets, e.g.: `%{}[a: 1, b: 2]` 163 | - Elixir: fixed: some multi-line when-type clauses were not matched correctly. 164 | - Elixir: don't match `def func()` as a type after writing a `|` in a type spec. 165 | - Elixir: use greedy matching for identifiers and atoms as well. 166 | - Elixir: the `meta.mapping.elixir` scope wasn't set correctly for maps. 167 | - SQL: fixed escapes breaking strings, e.g.: `fragment("? = '\"string\"'::jsonb", x)` 168 | 169 | ## [v2.0.4] – 2021-05-17 170 | 171 | - Elixir: use greedy matching for module names. 172 | 173 | ## [v2.0.3] – 2021-05-13 174 | 175 | - Elixir: moved syntax and color-scheme files into sub-folders. 176 | - Elixir: highlight EEx tags inside comments. 177 | 178 | ## [v2.0.2] – 2021-05-05 179 | 180 | - Elixir: fixed a bug where a comma was seen as invalid, such as in `if a == nil, ...`. 181 | - SQL: added decimal number highlighting. 182 | - SQL: added general function-call syntax matching. 183 | - SQL: removed `support.function.psql` scope to simplify the file. 184 | 185 | ## [v2.0.1] – 2021-05-02 186 | 187 | - Elixir: fixed a bug affecting, for example, multi-line `with` statements. 188 | - Elixir: fixed captures such as `&MyApp.User |> f(&1)`. 189 | - Elixir: fixed matching iex continuation lines (`...>`) in markdown comments. 190 | - SQL: highlight `BY` as a standalone keyword. 191 | 192 | ## [v2.0.0] – 2021-04-27 193 | 194 | The Elixir syntax definition has been reworked amounting to a complete rewrite 195 | bringing among other things the following features and improvements: 196 | 197 | - Type highlighting in `@spec`, `@type`, `@typep`, `@opaque`, `@callback` and `@macrocallback`. 198 | - Parameter highlighting (also in `fn`, `case`, `with`, `for`, `try` and `receive`). 199 | - Function call highlighting (e.g. `inspect error`, `Enum.map(...)`). 200 | - Highlight `as: A` argument in alias/require statements. 201 | - Better matching for `def`/`defmodule`/etc. statements. 202 | - Highlight quoted member variables/functions (e.g. `:erlang."=/="(1, 2)`). 203 | - Fixes to strings, numerics, escapes, captures etc. 204 | - Newest Elixir operators and functions. 205 | - Highlight LiveView, YAML and JSON strings. 206 | - Syntax definition for EEx in Elixir, e.g.: `defmodule <%= @module %>.View do end` 207 | - Some keywords are variables depending on the context (e.g. `def = 0`). 208 | - Markdown highlighting within `@doc` comments and also Elixir code examples. 209 | - Highlight SQL (e.g. jsonb operators) inside the Ecto `fragment()` function but also inside `sql("INSERT INTO ...")`. 210 | - Git merge conflicts are highlighted. 211 | - Added Monokai and Mariana theme color scheme rules. 212 | - Correct scope for symbol names for the "Goto Definition" command. 213 | - Speed optimizations: rules and regexps are ordered in such a way 214 | that they match the most likely occurring tokens first. 215 | - Snippets for `|> IO.inspect(label: "...")`, `|> then()` and `|> tap()`. 216 | - Extensive test-suite containing countless checks to ensure quality and avoid regressions. 217 | - Fixed Github CI syntax tests job. 218 | 219 | ## [v1.7.0] – 2020-05-20 220 | 221 | Thanks @dkarter 222 | - Allow leex file to be picked up 223 | 224 | ## [v1.6.0] – 2019-11-20 225 | 226 | Thanks to @azizk again 227 | - We now have regex syntax highlighting 228 | - EEx syntax highlighting now reuses html definition and interpolates Elixir parts into it 229 | - various fixes on general Elixir syntax definition 230 | - comprehensive sublime syntax tests 231 | 232 | ## [v1.3.0] – 2019-03-22 233 | 234 | Thanks to @azizk 235 | - Also apply syntax to files with elixirc and iex in hashbang 236 | - Module names can only have ASCII letters, fixed regex to do that 237 | - Highlight module names as normal atoms when found in map/kwlist keys 238 | - Highlight unicode identifers for atoms, variables and function names 239 | 240 | ## [v1.2.0] – 2019-02-13 241 | 242 | - Add Comment so we don't need https://github.com/elixir-editors/elixir-tmbundle alongside 243 | 244 | ## [v1.1.3] – 2019-02-01 245 | 246 | - Fix binary `^^^` highlight 247 | - Highlight more operators 248 | 249 | ## [v1.1.0] – 2017-12-13 250 | 251 | - Add HTML (EEX) 252 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Po Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ElixirSyntax 2 | 3 | *ElixirSyntax* was initially based on the [Elixir.tmbundle package](https://github.com/elixir-editors/elixir-tmbundle) but has been rewritten since, providing more accurate syntax matching as well as better syntax highlighting. 4 | 5 | ## Features 6 | 7 | * Working `Goto Definition` command. 8 | * HTML template highlighting: 9 | - HEEx: 10 | - Surface: 11 | - LiveView: 12 | * Full PCRE syntax highlighting: 13 | * Type highlighting: 14 | * Theme adaptations for Mariana and Monokai. 15 | * Palette commands: `ElixirSyntax: ...`, `Mix Test: ...`, `Mix Format: ...` 16 | * Build commands: `mix format`, `mix test`, `elixir $file` 17 | * Snippets for `IO.inspect`, `tap` and `then`. 18 | 19 | Some syntax highlighting features are not immediately evident. Among them are: 20 | 21 | ### The `fragment` and `sql` functions 22 | 23 | SQL syntax is highlighted inside Ecto's `fragment` macro. 24 | 25 |
26 | 27 |
28 | 29 | Add an `sql` macro/function to your project to enjoy SQL highlighting anywhere it's used. 30 | 31 |
32 | 33 |
34 | 35 | ### The JSON `~j` and `~J` sigils ([`Jason`](https://github.com/michalmuskala/jason/blob/master/lib/sigil.ex)) 36 | 37 | Embed JSON strings in your Elixir code. Notice the interpolated Elixir code is colored correctly. 38 | 39 |
40 | 41 |
42 | 43 | ### The YAML `~y` and `~Y` sigils ([`YamlElixir`](https://hexdocs.pm/yaml_elixir/YamlElixir.Sigil.html#sigil_y/2)) 44 | 45 |
46 | 47 |
48 | 49 | ## Testing 50 | 51 | Build-files as well as commands are provided for calling `mix test`. The predefined shortcuts can be changed via `Preferences > Package Settings > ElixirSyntax > Key Bindings`. 52 | 53 | Tip: To run specific tests in the current file, mark them with multiple cursors and/or spanning selections and press `Alt+Shift+T` or choose `Mix Test: Selection(s)` from the palette. 54 | 55 | *ElixirSyntax* stores a per-project JSON settings file in the root folder that contains both the `mix.exs` file and the `_build/` folder. They override the general settings below. 56 | 57 | General settings example (via `Preferences > Package Settings > ElixirSyntax > Settings`): 58 | ```json 59 | { 60 | "mix_test": { 61 | "output": "tab", 62 | "output_mode": null, 63 | "args": ["--coverage"], 64 | "seed": null 65 | } 66 | } 67 | ``` 68 | 69 | When a `mix test` command is run the first time, a `mix_test.repeat.json` file is stored in the `_build/` folder to remember the command arguments. By pressing `Alt+Shift+R` or running `Mix Test: Repeat` from the palette you can repeat the previously executed tests. 70 | 71 | ## Formatting 72 | 73 | Use the default shortcut `Alt+Shift+F` or the palette command `Mix Format: File` to format your Elixir code. Format the whole project via `Mix Format: Project / Folder`. Configure auto-formatting on save via the palette command `Mix Format: Toggle Auto-Formatting` or via the menu `Preferences > Package Settings > ElixirSyntax > Settings`. There is no per-project auto-format setting yet. 74 | 75 | ```json 76 | { 77 | "mix_format": { 78 | "on_save": true 79 | } 80 | } 81 | ``` 82 | 83 | ## Palette commands 84 | 85 | - `ElixirSyntax: Settings` 86 | - `ElixirSyntax: Open Hex Docs` 87 | - `ElixirSyntax: Search Hex Packages` 88 | - `Mix Test: Settings` 89 | - `Mix Test: All` 90 | - `Mix Test: File` 91 | - `Mix Test: Selection(s)` 92 | - `Mix Test: Failed` 93 | - `Mix Test: Repeat` 94 | - `Mix Test: Set --seed` 95 | - `Mix Test: Toggle --stale Flag` 96 | - `Mix Test: Switch to Code or Test` 97 | - `Mix Test: Show Panel` 98 | - `Mix Format: File` 99 | - `Mix Format: Project / Folder` 100 | - `Mix Format: Toggle Auto-Formatting` 101 | 102 | ## Recommended packages 103 | 104 | * [LSP](https://packagecontrol.io/packages/LSP) and [LSP-elixir](https://packagecontrol.io/packages/LSP-elixir) for intelligent code completion and additional snippet suggestions. 105 | 106 | ## Changes 107 | 108 | See [CHANGELOG.md](./CHANGELOG.md) for the list of releases and noteworthy changes. 109 | 110 | ## FAQ 111 | 112 | - How to color unused variables, e.g. `_opts`, differently? 113 | 114 | You can [customize the color](https://user-images.githubusercontent.com/1329716/152258038-384c6a61-d974-4e9a-a1db-ab979c839ff7.png) of unused variable names by extending your color scheme, targeting the `variable.parameter.unused` and `variable.other.unused` scopes: 115 | 116 | ```json 117 | { 118 | "rules": [ 119 | { 120 | "name": "Unused variables", 121 | "scope": "variable.parameter.unused, variable.other.unused", 122 | "foreground": "#8c8cff" 123 | } 124 | ] 125 | } 126 | ``` 127 | 128 | More details at [Sublime Text Docs](https://www.sublimetext.com/docs/color_schemes.html) 129 | 130 | ## Contributors/Maintainers 131 | 132 | - [@azizk](https://github.com/azizk) rewrote the whole syntax definition with an extensive test-suite and added a wealth of new features. ⭐ 133 | - [@princemaple](https://github.com/princemaple) initially brought the tm-syntax to sublime-syntax and made some improvements. 134 | -------------------------------------------------------------------------------- /builds/Elixir - elixir FILE.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["elixir", "$file"], 3 | "selector": "source.elixir", 4 | "windows": { 5 | "working_dir": "$file_path", 6 | "cmd": ["elixir.bat", "$file_name"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /builds/Elixir - elixirc $file.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["elixirc", "$file"], 3 | "selector": "source.elixir", 4 | "windows": { 5 | "working_dir": "$file_path", 6 | "cmd": ["elixirc.bat", "$file_name"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /builds/Elixir - mix compile.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["mix", "compile"], 3 | "working_dir": "${project_path:${folder}}", 4 | "selector": "source.elixir", 5 | "windows": { 6 | "cmd": ["mix.bat", "compile"] 7 | }, 8 | "variants": [ 9 | { 10 | "name": "Dependencies", 11 | "cmd": ["mix", "deps.compile"], 12 | "windows": { 13 | "cmd": ["mix.bat", "deps.compile"] 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /builds/Elixir - mix format FILE.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "working_dir": "$folder", 3 | "cmd": ["mix", "format", "$file", "--dot-formatter", "$folder/.formatter.exs"], 4 | "selector": "source.elixir", 5 | "windows": { 6 | "cmd": ["mix.bat", "format", "$file", "--dot-formatter", "$folder/.formatter.exs"], 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /builds/Elixir - mix test FILE.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "target": "mix_test_file", 3 | "working_dir": "${project_path}", 4 | "file_patterns": ["*_test.exs"] 5 | } 6 | -------------------------------------------------------------------------------- /builds/Elixir - mix test.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "target": "mix_test", 3 | "working_dir": "${project_path}" 4 | } 5 | -------------------------------------------------------------------------------- /color-schemes/Mariana.sublime-color-scheme: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mariana for Elixir", 3 | "variables": 4 | { 5 | "doc": "var(blue6)" 6 | }, 7 | "rules": 8 | [ 9 | // NB: commented out. See: "Embedded punctuation" 10 | // { 11 | // "name": "EEx tag", 12 | // "scope": "punctuation.section.embedded.begin.eex | punctuation.section.embedded.end.eex", 13 | // "foreground": "var(blue5)" 14 | // }, 15 | { 16 | "name": "EEx embedded", 17 | "scope": "source.elixir.embedded.html", 18 | "background": "color(var(white) a(0.05))" 19 | }, 20 | { 21 | "name": "Doc attribute", 22 | "scope": "support.attr.doc.elixir", 23 | "foreground": "var(doc)" 24 | }, 25 | { 26 | "name": "Doc string", 27 | "scope": "meta.doc.elixir meta.string.elixir string | meta.doc meta.string.elixir punctuation.definition.string | meta.doc.elixir storage.type.string.elixir", 28 | "foreground": "var(doc)" 29 | }, 30 | { 31 | "name": "Doc string escapes", 32 | "scope": "meta.doc.elixir meta.string.elixir constant.character.escape.char.elixir", 33 | "foreground": "color(var(doc) l(+ 15%))" 34 | }, 35 | { 36 | "name": "Support attributes", 37 | "scope": "support.attr.elixir", 38 | "foreground": "var(blue)" 39 | }, 40 | { 41 | "name": "Constant variable", 42 | "scope": "variable.other.constant.elixir", 43 | "foreground": "color(var(pink))" 44 | }, 45 | { 46 | "name": "Unused variable", 47 | "scope": "variable.other.unused.elixir", 48 | "foreground": "color(var(white) a(0.6))" 49 | }, 50 | { 51 | "name": "Unused parameter", 52 | "scope": "variable.parameter.unused.elixir", 53 | "foreground": "color(var(orange) a(0.6))" 54 | }, 55 | { 56 | "name": "Spec name", 57 | "scope": "variable.other.spec.elixir", 58 | "font_style": "italic", 59 | "foreground": "color(var(blue5) s(- 50%))" 60 | }, 61 | { 62 | "name": "Capture name", 63 | "scope": "variable.other.capture.elixir", 64 | "foreground": "color(var(blue))", 65 | "font_style": "italic" 66 | }, 67 | { 68 | "name": "Capture arity", 69 | "scope": "constant.numeric.arity.elixir", 70 | "font_style": "italic" 71 | }, 72 | { 73 | "name": "Capture placeholder ampersand", 74 | "scope": "punctuation.definition.capture.elixir constant.other.capture.elixir", 75 | "foreground": "color(var(pink) s(- 30%))" 76 | }, 77 | { 78 | "name": "Module atom", 79 | "scope": "constant.other.module.elixir", 80 | "foreground": "color(var(orange) s(+ 50%) l(- 20%))" 81 | }, 82 | { 83 | "name": "Atom keyword", 84 | "scope": "constant.other.keyword.elixir", 85 | "foreground": "color(var(pink) s(- 10%) l(- 15%))" 86 | }, 87 | { 88 | "name": "Atom keyword punctuation", 89 | "scope": "punctuation.definition.constant", 90 | "foreground": "color(var(white) a(0.65))" 91 | }, 92 | { 93 | "name": "Numeric punctuation", 94 | "scope": "punctuation.separator.numeric.elixir | punctuation.definition.numeric.elixir | punctuation.separator.decimal.elixir", 95 | "foreground": "color(var(pink) s(- 30%))" 96 | }, 97 | { 98 | "name": "Escapes in remote function call", 99 | "scope": "variable.function.elixir constant.character.escape.char.elixir", 100 | "foreground": "color(var(blue) l(+ 10%))" 101 | }, 102 | { 103 | "name": "Function punctuation", 104 | "scope": "keyword.declaration.function.elixir punctuation.section", 105 | "foreground": "var(pink)" 106 | }, 107 | { 108 | "name": "Embedded or interpolated Elixir", 109 | "scope": "source.elixir.embedded | source.elixir.interpolated", 110 | "background": "color(var(white) a(0.03))" 111 | }, 112 | { 113 | "name": "Interpolation punctuation", 114 | "scope": "punctuation.section.interpolation.begin.elixir | punctuation.section.interpolation.end.elixir", 115 | "foreground": "color(var(white) l(- 30%))" 116 | }, 117 | { 118 | "name": "Embedded punctuation", 119 | "scope": "punctuation.section.embedded.begin.elixir | punctuation.section.embedded.end.elixir | punctuation.section.embedded.begin.eex | punctuation.section.embedded.end.eex | punctuation.section.embedded.begin.surface | punctuation.section.embedded.end.surface", 120 | "foreground": "color(var(white) l(- 30%))" 121 | }, 122 | { 123 | "name": "Surface comment punctuation", 124 | "scope": "punctuation.definition.comment.begin.surface | punctuation.definition.comment.end.surface", 125 | "foreground": "color(var(white) l(- 30%))" 126 | }, 127 | { 128 | "name": "SQL boolean", 129 | "scope": "constant.boolean.sql", 130 | "font_style": "italic", 131 | "foreground": "color(var(red))" 132 | }, 133 | { 134 | "name": "SQL embedded", 135 | "scope": "source.elixir source.sql", 136 | "background": "color(var(white) a(0.03))" 137 | }, 138 | { 139 | "name": "Types", 140 | "scope": "meta.type.elixir", 141 | "font_style": "italic" 142 | }, 143 | { 144 | "name": "Type parameters", 145 | "scope": "meta.type.elixir & (variable.parameter.elixir | variable.parameter.unused.elixir)", 146 | "font_style": "italic" 147 | }, 148 | { 149 | "name": "Type pipe, colon, string", 150 | "scope": "meta.type.elixir & (keyword.operator.union | keyword.operator.colon | string)", 151 | "font_style": "regular" 152 | }, 153 | { 154 | "name": "Type label", 155 | "scope": "variable.other.named-type.elixir", 156 | "foreground": "color(var(white) l(- 40%))" 157 | }, 158 | { 159 | "name": "Type identifier", 160 | "scope": "support.type.elixir | storage.type.custom.elixir | storage.type.remote.elixir | storage.type.binary.elixir", 161 | "foreground": "color(var(pink) s(- 40%))" 162 | }, 163 | { 164 | "name": "Type in binary", 165 | "scope": "meta.type.binary.elixir -keyword.operator.colon.elixir", 166 | "font_style": "italic" 167 | }, 168 | { 169 | "name": "String sigil", 170 | "scope": "storage.type.string.elixir", 171 | "font_style": "regular" 172 | }, 173 | // { 174 | // "name": "Darker string punctuation", 175 | // "scope": "source.elixir punctuation.definition.string", 176 | // "foreground": "color(var(blue) l(- 20%))" 177 | // }, 178 | { 179 | "name": "Group parentheses", 180 | "scope": "punctuation.section.group.begin.elixir | punctuation.section.group.end.elixir", 181 | "foreground": "color(var(white) a(0.7))" 182 | }, 183 | { 184 | "name": "Item access brackets", 185 | "scope": "punctuation.section.access.begin.elixir | punctuation.section.access.end.elixir", 186 | "foreground": "var(red)" 187 | }, 188 | { 189 | "name": "Line continuation", 190 | "scope": "punctuation.separator.continuation.elixir", 191 | "foreground": "color(var(white) a(0.3))" 192 | }, 193 | { 194 | "name": "Git merge conflict current", 195 | "scope": "punctuation.section.block.begin.git.conflict", 196 | "background": "#565", 197 | "font_style": "bold", 198 | "foreground": "var(white)" 199 | }, 200 | { 201 | "name": "Git merge conflict current", 202 | "scope": "comment.line.current.git.conflict", 203 | "background": "#454", 204 | "foreground": "color(var(white) a(0.8))" 205 | }, 206 | { 207 | "name": "Git merge conflict middle", 208 | "scope": "punctuation.section.block.middle.git.conflict", 209 | "background": "color(var(white) a(0.1))", 210 | "font_style": "bold", 211 | "foreground": "var(white)" 212 | }, 213 | { 214 | "name": "Git merge conflict middle", 215 | "scope": "comment.line.middle.git.conflict", 216 | "background": "color(var(white) a(0.05))", 217 | "foreground": "color(var(white) a(0.8))" 218 | }, 219 | { 220 | "name": "Git merge conflict incoming", 221 | "scope": "punctuation.section.block.end.git.conflict", 222 | "background": "#557", 223 | "font_style": "bold", 224 | "foreground": "var(white)" 225 | }, 226 | { 227 | "name": "Git merge conflict incoming", 228 | "scope": "comment.line.incoming.git.conflict", 229 | "background": "#446", 230 | "foreground": "color(var(white) a(0.8))" 231 | } 232 | ] 233 | } 234 | -------------------------------------------------------------------------------- /color-schemes/Monokai.sublime-color-scheme: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Monokai for Elixir", 3 | "variables": 4 | { 5 | "white3": "#ddd", 6 | "entity": "var(yellow2)", 7 | "doc": "var(yellow5)" 8 | }, 9 | "rules": 10 | [ 11 | // NB: commented out to keep it similar to the style of HTML tags. 12 | // { 13 | // "name": "EEx tag", 14 | // "scope": "punctuation.section.embedded.begin.eex | punctuation.section.embedded.end.eex", 15 | // "foreground": "var(red)" 16 | // }, 17 | { 18 | "name": "EEx embedded", 19 | "scope": "source.elixir.embedded.html", 20 | "background": "color(var(white) a(0.05))" 21 | }, 22 | { 23 | "name": "Doc attribute", 24 | "scope": "support.attr.doc.elixir", 25 | "foreground": "var(doc)" 26 | }, 27 | { 28 | "name": "Doc string", 29 | "scope": "meta.doc.elixir meta.string.elixir string | meta.doc meta.string.elixir punctuation.definition.string | meta.doc.elixir storage.type.string.elixir", 30 | "foreground": "var(doc)" 31 | }, 32 | { 33 | "name": "Doc string escapes", 34 | "scope": "meta.doc.elixir meta.string.elixir constant.character.escape.char.elixir", 35 | "foreground": "color(var(doc) l(+ 15%))" 36 | }, 37 | { 38 | "name": "Support attributes", 39 | "scope": "support.attr.elixir", 40 | "foreground": "var(blue)" 41 | }, 42 | { 43 | "name": "Constant variable", 44 | "scope": "variable.other.constant.elixir", 45 | "foreground": "color(var(purple))" 46 | }, 47 | { 48 | "name": "Unused variable", 49 | "scope": "variable.other.unused.elixir", 50 | "foreground": "color(var(white) a(0.5))" 51 | }, 52 | { 53 | "name": "Unused parameter", 54 | "scope": "variable.parameter.unused.elixir", 55 | "foreground": "color(var(orange) a(0.6))", 56 | "font_style": "regular" 57 | }, 58 | { 59 | "name": "Parameter", 60 | "scope": "variable.parameter.elixir", 61 | "font_style": "regular" 62 | }, 63 | { 64 | "name": "Spec name", 65 | "scope": "variable.other.spec.elixir", 66 | "font_style": "italic", 67 | "foreground": "color(var(entity) s(- 50%))" 68 | }, 69 | { 70 | "name": "Capture name", 71 | "scope": "variable.other.capture.elixir", 72 | "foreground": "color(var(blue))", 73 | "font_style": "italic" 74 | }, 75 | { 76 | "name": "Capture arity", 77 | "scope": "constant.numeric.arity.elixir", 78 | "font_style": "italic" 79 | }, 80 | { 81 | "name": "Capture placeholder ampersand", 82 | "scope": "punctuation.definition.capture.elixir constant.other.capture.elixir", 83 | "foreground": "color(var(purple) s(- 30%))" 84 | }, 85 | { 86 | "name": "Module atom", 87 | "scope": "constant.other.module.elixir", 88 | "foreground": "color(var(entity) s(+ 50%) l(- 20%))" 89 | }, 90 | { 91 | "name": "Atom keyword", 92 | "scope": "constant.other.keyword.elixir", 93 | "foreground": "color(var(purple) s(- 50%) l(- 15%))" 94 | }, 95 | { 96 | "name": "Atom keyword punctuation", 97 | "scope": "punctuation.definition.constant", 98 | "foreground": "color(var(white) a(0.65))" 99 | }, 100 | { 101 | "name": "Numeric punctuation", 102 | "scope": "punctuation.separator.numeric.elixir | punctuation.definition.numeric.elixir | punctuation.separator.decimal.elixir", 103 | "foreground": "color(var(purple) s(- 30%))" 104 | }, 105 | { 106 | "name": "Escapes in remote function call", 107 | "scope": "variable.function.elixir constant.character.escape.char.elixir", 108 | "foreground": "color(var(blue) l(- 30%))" 109 | }, 110 | { 111 | "name": "Embedded or interpolated Elixir", 112 | "scope": "source.elixir.embedded | source.elixir.interpolated", 113 | "background": "color(var(white) a(0.03))" 114 | }, 115 | { 116 | "name": "Interpolation punctuation", 117 | "scope": "punctuation.section.interpolation.begin.elixir | punctuation.section.interpolation.end.elixir", 118 | "foreground": "color(var(white) l(- 30%))" 119 | }, 120 | { 121 | "name": "Embedded punctuation", 122 | "scope": "punctuation.section.embedded.begin.elixir | punctuation.section.embedded.end.elixir | punctuation.section.embedded.begin.eex | punctuation.section.embedded.end.eex | punctuation.section.embedded.begin.surface | punctuation.section.embedded.end.surface", 123 | "foreground": "color(var(white) l(- 30%))" 124 | }, 125 | { 126 | "name": "Surface comment punctuation", 127 | "scope": "punctuation.definition.comment.begin.surface | punctuation.definition.comment.end.surface", 128 | "foreground": "color(var(white) l(- 30%))" 129 | }, 130 | { 131 | "name": "SQL boolean", 132 | "scope": "constant.boolean.sql", 133 | "foreground": "color(var(purple))" 134 | }, 135 | { 136 | "name": "SQL embedded", 137 | "scope": "source.elixir source.sql", 138 | "background": "color(var(white) a(0.03))" 139 | }, 140 | { 141 | "name": "Types", 142 | "scope": "meta.type.elixir", 143 | "font_style": "italic" 144 | }, 145 | { 146 | "name": "Type parameters", 147 | "scope": "meta.type.elixir & (variable.parameter.elixir | variable.parameter.unused.elixir)", 148 | "font_style": "italic" 149 | }, 150 | { 151 | "name": "Type pipe, colon, string", 152 | "scope": "meta.type.elixir & (keyword.operator.union | keyword.operator.colon | string)", 153 | "font_style": "regular" 154 | }, 155 | { 156 | "name": "Type label", 157 | "scope": "variable.other.named-type.elixir", 158 | "foreground": "color(var(white) l(- 40%))" 159 | }, 160 | { 161 | "name": "Type identifier", 162 | "scope": "support.type.elixir | storage.type.custom.elixir | storage.type.remote.elixir | storage.type.binary.elixir", 163 | "foreground": "color(var(blue) s(- 40%))" 164 | }, 165 | { 166 | "name": "Type in binary", 167 | "scope": "meta.type.binary.elixir -keyword.operator.colon.elixir", 168 | "font_style": "italic" 169 | }, 170 | { 171 | "name": "String sigil", 172 | "scope": "storage.type.string.elixir", 173 | "font_style": "regular" 174 | }, 175 | { 176 | "name": "Darker string punctuation", 177 | "scope": "source.elixir punctuation.definition.string", 178 | "foreground": "color(var(yellow) l(- 20%))" 179 | }, 180 | { 181 | "name": "Group parentheses", 182 | "scope": "punctuation.section.group.begin.elixir | punctuation.section.group.end.elixir", 183 | "foreground": "color(var(white) a(0.7))" 184 | }, 185 | { 186 | "name": "Item access brackets", 187 | "scope": "punctuation.section.access.begin.elixir | punctuation.section.access.end.elixir", 188 | "foreground": "var(red)" 189 | }, 190 | { 191 | "name": "Line continuation", 192 | "scope": "punctuation.separator.continuation.elixir", 193 | "foreground": "color(var(white) a(0.3))" 194 | }, 195 | { 196 | "name": "Git merge conflict current", 197 | "scope": "punctuation.section.block.begin.git.conflict", 198 | "background": "#454", 199 | "font_style": "bold", 200 | "foreground": "var(white)" 201 | }, 202 | { 203 | "name": "Git merge conflict current", 204 | "scope": "comment.line.current.git.conflict", 205 | "background": "#343", 206 | "foreground": "color(var(white) a(0.8))" 207 | }, 208 | { 209 | "name": "Git merge conflict middle", 210 | "scope": "punctuation.section.block.middle.git.conflict", 211 | "background": "color(var(white) a(0.1))", 212 | "font_style": "bold", 213 | "foreground": "var(white)" 214 | }, 215 | { 216 | "name": "Git merge conflict middle", 217 | "scope": "comment.line.middle.git.conflict", 218 | "background": "color(var(white) a(0.05))", 219 | "foreground": "color(var(white) a(0.8))" 220 | }, 221 | { 222 | "name": "Git merge conflict incoming", 223 | "scope": "punctuation.section.block.end.git.conflict", 224 | "background": "#446", 225 | "font_style": "bold", 226 | "foreground": "var(white)" 227 | }, 228 | { 229 | "name": "Git merge conflict incoming", 230 | "scope": "comment.line.incoming.git.conflict", 231 | "background": "#335", 232 | "foreground": "color(var(white) a(0.8))" 233 | } 234 | ] 235 | } 236 | -------------------------------------------------------------------------------- /commands/Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { "caption": "ElixirSyntax: Settings", "command": "edit_settings", "args": { 3 | "base_file": "${packages}/ElixirSyntax/settings/ElixirSyntax.sublime-settings", 4 | "default": "{\n $0\n}\n" 5 | } }, 6 | { "caption": "ElixirSyntax: Open Hex Docs", "command": "open_hex_docs" }, 7 | { "caption": "ElixirSyntax: Search Hex Packages", "command": "search_hex_packages" }, 8 | { "caption": "Mix Test: Settings", "command": "mix_test_settings" }, 9 | { "caption": "Mix Test: All", "command": "mix_test" }, 10 | { "caption": "Mix Test: File", "command": "mix_test_file" }, 11 | { "caption": "Mix Test: Selection(s)", "command": "mix_test_selection" }, 12 | { "caption": "Mix Test: Failed", "command": "mix_test_failed" }, 13 | { "caption": "Mix Test: Repeat", "command": "mix_test_repeat" }, 14 | { "caption": "Mix Test: Set --seed", "command": "mix_test_set_seed" }, 15 | { "caption": "Mix Test: Toggle --stale Flag", "command": "mix_test_toggle_stale_flag" }, 16 | { "caption": "Mix Test: Switch to Code or Test", "command": "mix_test_switch_to_code_or_test" }, 17 | { "caption": "Mix Test: Show Panel", "command": "mix_test_show_panel" }, 18 | { "caption": "Mix Test: Hide Panel", "command": "mix_test_hide_panel" }, 19 | { "caption": "Mix Format: File", "command": "mix_format_file" }, 20 | { "caption": "Mix Format: Project / Folder", "command": "mix_format_project" }, 21 | { "caption": "Mix Format: Toggle Auto-Formatting", "command": "mix_format_toggle_auto_format" }, 22 | ] 23 | -------------------------------------------------------------------------------- /commands/__init__.py: -------------------------------------------------------------------------------- 1 | from .hex_packages import * 2 | from .mix_test import * 3 | from .mix_format import * 4 | -------------------------------------------------------------------------------- /commands/hex_packages.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import re 4 | import webbrowser 5 | 6 | from pathlib import Path 7 | from urllib import request 8 | from urllib.error import HTTPError 9 | from datetime import datetime 10 | from .utils import * 11 | 12 | __author__ = 'Aziz Köksal' 13 | __email__ = 'aziz.koeksal@gmail.com' 14 | __status__ = 'Production' 15 | 16 | HEXDOCS_URL = 'https://hexdocs.pm' 17 | HEX_URL = 'https://hex.pm' 18 | ELIXIR_CORE_APP_NAMES = ['eex', 'elixir', 'ex_unit', 'hex', 'iex', 'logger', 'mix'] 19 | name_lastmod_rx = r'hexdocs.pm/([^/]+)/[\s\S]+?([^<]+)' 20 | PROJECT_MAX_AGE_DAYS = 365 21 | 22 | class SearchHexPackagesCommand(sublime_plugin.WindowCommand): 23 | def description(self): 24 | return 'Searches hex.pm and shows the results.' 25 | 26 | def run(self, **kwargs): 27 | query = (kwargs.get('query') or '').strip() 28 | 29 | if query: 30 | print_status_msg('Searching hex.pm for %r' % query) 31 | sublime.set_timeout_async(lambda: search_hex_pm(self.window, query)) 32 | 33 | def input(self, _args): 34 | class QueryInputHandler(sublime_plugin.TextInputHandler): 35 | def placeholder(self): return 'Search hex.pm' 36 | def validate(self, text): return text.strip() != '' 37 | 38 | return QueryInputHandler() 39 | 40 | class OpenHexDocsCommand(sublime_plugin.WindowCommand): 41 | def description(self): 42 | return 'Finds and opens hex documentation in the browser.' 43 | 44 | def run(self, **_kwargs): 45 | cache_dir = Path(sublime.cache_path(), 'ElixirSyntax') 46 | cache_dir.exists() or cache_dir.mkdir(parents=True) 47 | 48 | cached_sitemap_json_path = Path(cache_dir, 'hexdocs.sitemap.json') 49 | 50 | sitemap_dict = {} 51 | sitemap_url = HEXDOCS_URL + '/sitemap.xml' 52 | 53 | if cached_sitemap_json_path.exists(): 54 | sitemap_dict = load_json_file(cached_sitemap_json_path) 55 | etag = sitemap_dict['etag'] 56 | 57 | def refresh_sitemap(): 58 | try: 59 | resp = request.urlopen(request.Request(sitemap_url, headers={'If-None-Match': etag})) 60 | sitemap_dict = fetch_parse_and_save_sitemap(resp, cached_sitemap_json_path) 61 | show_hexdocs_list(self.window, sitemap_dict.get('projects', [])) 62 | except HTTPError as e: 63 | e.code == 304 or print_status_msg('Error: %s' % e) 64 | 65 | sublime.set_timeout_async(refresh_sitemap) 66 | 67 | show_hexdocs_list(self.window, sitemap_dict.get('projects', [])) 68 | else: 69 | print_status_msg('Downloading %r' % sitemap_url) 70 | 71 | def fetch_sitemap(): 72 | try: 73 | resp = request.urlopen(sitemap_url) 74 | sitemap_dict = fetch_parse_and_save_sitemap(resp, cached_sitemap_json_path) 75 | show_hexdocs_list(self.window, sitemap_dict.get('projects', [])) 76 | except HTTPError as e: 77 | print_status_msg('Error: could not fetch %r (status=#%s)' % (sitemap_url, resp.code)) 78 | 79 | sublime.set_timeout_async(fetch_sitemap) 80 | 81 | def search_hex_pm(window, query, **kwargs): 82 | """ Searches hex.pm and shows the results in a quick panel overlay. """ 83 | page = kwargs.get('page') 84 | page_param = page and ['page=%s' % page] or [] 85 | query = query and ''.join('%%%x' % ord(c) if c in '#&/?' else c for c in query) 86 | get_params = '&'.join(['search=%s' % query, 'sort=recent_downloads'] + page_param) 87 | query_url = HEX_URL + '/packages?' + get_params 88 | resp = request.urlopen(query_url) 89 | results_html = resp.read().decode('utf-8') 90 | 91 | package_list_match = re.search(r'
([\s\S]+?)\n
', results_html) 92 | page_match = re.search(r'
  • [\s\S]+?
  • [\s\S]+?\bpage=(\d+)', results_html) 93 | next_page = page_match and int(page_match.group(1)) 94 | total_count_match = re.search(r'packages of (\d+) total', results_html) 95 | total_packages_count = total_count_match and total_count_match.group(1) 96 | 97 | if not package_list_match: 98 | has_no_results = 'no-results' in results_html 99 | 100 | msg = [ 101 | 'could not find div.package-list in the results HTML.', 102 | 'no results found for %r on hex.pm!' % query 103 | ][has_no_results] 104 | 105 | if has_no_results: 106 | overlay_args = {'overlay': 'command_palette', 'command': 'search_hex_packages'} 107 | window.run_command('show_overlay', overlay_args) 108 | window.run_command('insert', {'characters': query}) 109 | 110 | print_status_msg('Error: ' + msg) 111 | return 112 | 113 | package_matches = re.findall(r'''(?xi) 114 | (.+?) [\s\S]*? 115 | total\sdownloads:\s (.+?) [\s\S]*? 116 | (.+?) [\s\S]*? 117 | (.+?) [\s\S]*? 118 |

    ([^<]*)

    119 | ''', 120 | package_list_match.group(1) 121 | ) 122 | 123 | previous_results = kwargs.get('previous_results') or [] 124 | 125 | results = previous_results + [ 126 | {'name': m[2], 'desc': m[4], 'version': m[3], 'recent_dls': m[0], 'total_dls': m[1], 127 | 'url': HEX_URL + '/packages/' + m[2]} 128 | for m in package_matches 129 | ] 130 | 131 | selectable_results = results + [ 132 | {'label': 'Open search query in browser', 'url': query_url, 'desc': 'Terms: %s' % query}, 133 | ] + ( 134 | next_page and [{ 135 | 'label': 'Load page %d' % next_page, 136 | 'page': next_page, 137 | 'desc': 'Total packages found: %s' % (total_packages_count or 'unknown') 138 | }] or [] 139 | ) 140 | 141 | def on_select(i): 142 | if i >= 0: 143 | result = selectable_results[i] 144 | if result.get('page'): 145 | print_status_msg('Loading page %d on hex.pm for %r' % (next_page, query)) 146 | cb = lambda: search_hex_pm(window, query, page=next_page, previous_results=results) 147 | sublime.set_timeout_async(cb) 148 | else: 149 | webbrowser.open_new_tab(result['url']) 150 | 151 | placeholder = 'Open a project in the web browser.' 152 | selected_index = len(previous_results) if previous_results else -1 153 | 154 | result_items = [ 155 | sublime.QuickPanelItem( 156 | trigger=result.get('label') or '%s v%s' % (result['name'], result['version']), 157 | details=result.get('desc') or '', 158 | annotation=result.get('recent_dls') \ 159 | and '%s recent / %s total downloads' % (result['recent_dls'], result['total_dls']) \ 160 | or '', 161 | kind=result.get('recent_dls') and sublime.KIND_NAVIGATION or sublime.KIND_AMBIGUOUS 162 | ) 163 | for result in selectable_results 164 | ] 165 | 166 | window.show_quick_panel(result_items, on_select, 167 | placeholder=placeholder, selected_index=selected_index 168 | ) 169 | 170 | def fetch_parse_and_save_sitemap(resp, cached_sitemap_json_path): 171 | """ Fetches, parses and saves the sitemap items in a JSON file. """ 172 | etag = next( 173 | (value for (header, value) in resp.headers.items() if header.lower() == 'etag'), None 174 | ) 175 | 176 | sitemap_xml = resp.read().decode('utf-8') 177 | elixir_core_projects = [(name, None) for name in ELIXIR_CORE_APP_NAMES] 178 | hexdocs_projects = re.findall(name_lastmod_rx, sitemap_xml) 179 | young_projects, old_projects, now = [], [], datetime.now() 180 | 181 | for name, date in hexdocs_projects: 182 | parsed_date = datetime.strptime(date[:10], '%Y-%m-%d') 183 | younger_than_x_days = (now - parsed_date).days <= PROJECT_MAX_AGE_DAYS 184 | (young_projects if younger_than_x_days else old_projects).append((name, date)) 185 | 186 | projects = sorted(young_projects + elixir_core_projects) + old_projects 187 | projects = [{'name': name, 'lastmod': lastmod} for (name, lastmod) in projects] 188 | sitemap_dict = {'projects': projects, 'etag': etag} 189 | save_json_file(cached_sitemap_json_path, sitemap_dict) 190 | 191 | return sitemap_dict 192 | 193 | def show_hexdocs_list(window, projects): 194 | """ Shows the hexdocs projects in a quick panel overlay. """ 195 | project_items = [ 196 | sublime.QuickPanelItem( 197 | trigger=project['name'], 198 | details=project['lastmod'] \ 199 | and 'Last modified: %s' % project['lastmod'][:-4].replace('T', ' ') \ 200 | or '', 201 | kind=sublime.KIND_NAVIGATION 202 | ) 203 | for project in projects 204 | ] 205 | 206 | def on_select(i): 207 | i >= 0 and webbrowser.open_new_tab(HEXDOCS_URL + '/' + projects[i]['name']) 208 | 209 | placeholder = 'Open a project\'s documentation in the web browser.' 210 | window.show_quick_panel(project_items, on_select, placeholder=placeholder) 211 | -------------------------------------------------------------------------------- /commands/mix_format.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import subprocess 4 | import shlex 5 | from .utils import * 6 | from time import time as now 7 | from datetime import datetime 8 | 9 | __author__ = 'Aziz Köksal' 10 | __email__ = 'aziz.koeksal@gmail.com' 11 | __status__ = 'Production' 12 | 13 | class MixFormatProjectCommand(sublime_plugin.WindowCommand): 14 | def description(self): 15 | return 'Runs `mix format` on the project path or the opened folder.' 16 | 17 | def run(self, **_kwargs): 18 | call_mix_format_async(self.window) 19 | 20 | class MixFormatFileCommand(sublime_plugin.TextCommand): 21 | def description(self): 22 | return 'Runs `mix format` on the current file.' 23 | 24 | def run(self, _edit, **kwargs): 25 | window = self.view.window() 26 | file_path = self.view.file_name() 27 | kwargs.get('save', True) and window.run_command('save') 28 | call_mix_format_async(window, file_path=file_path) 29 | 30 | def is_enabled(self): 31 | return is_formattable_syntax(self.view) 32 | 33 | class MixFormatToggleAutoFormatCommand(sublime_plugin.TextCommand): 34 | def description(self): 35 | return 'Enables or disables auto-formatting on save.' 36 | 37 | def run(self, _edit, **_kwargs): 38 | package_settings, mix_format_settings = load_mix_format_settings() 39 | on_save = mix_format_settings['on_save'] = not mix_format_settings.get('on_save', False) 40 | package_settings.set('mix_format', mix_format_settings) 41 | sublime.save_settings(SETTINGS_FILE_NAME) 42 | print_status_msg('%s auto-formatting!' % ['Disabled', 'Enabled'][on_save]) 43 | 44 | def is_enabled(self): 45 | return is_formattable_syntax(self.view) 46 | 47 | class MixFormatOnSaveListener(sublime_plugin.EventListener): 48 | def is_elixir_file(self, view): 49 | return is_formattable_syntax(view) 50 | 51 | def on_post_save(self, view): 52 | if not self.is_elixir_file(view): 53 | return 54 | _, mix_format_settings = load_mix_format_settings() 55 | if mix_format_settings.get('on_save', False): 56 | MixFormatFileCommand(view).run(None, save=False) 57 | if mix_format_settings.get('reload_after', False): 58 | view.run_command('revert') 59 | 60 | def load_mix_format_settings(): 61 | package_settings = sublime.load_settings(SETTINGS_FILE_NAME) 62 | return (package_settings, package_settings.get('mix_format', {})) 63 | 64 | def call_mix_format_async(window, **kwargs): 65 | file_path = kwargs.get('file_path') 66 | print_status_msg('Formatting %s!' % (file_path and repr(file_path) or 'project')) 67 | sublime.set_timeout_async(lambda: call_mix_format(window, **kwargs)) 68 | 69 | def call_mix_format(window, **kwargs): 70 | file_path = kwargs.get('file_path') 71 | file_path_list = file_path and [file_path] or [] 72 | _, cmd_setting = load_mix_format_settings() 73 | cmd_args = (cmd_setting.get('cmd') or ['mix', 'format']) + file_path_list 74 | 75 | paths = file_path_list + window.folders() 76 | cwd = next((reverse_find_root_folder(p) for p in paths if p), None) 77 | 78 | if not (cwd or file_path): 79 | print_status_msg(COULDNT_FIND_MIX_EXS) 80 | return 81 | 82 | proc = subprocess.Popen(cmd_args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0) 83 | 84 | panel_name = 'mix_format' 85 | panel_params = {'panel': 'output.%s' % panel_name} 86 | window.run_command('erase_view', panel_params) 87 | output_view = None 88 | failed_msg_region = None 89 | 90 | try: 91 | for text in read_proc_text_output(proc): 92 | if not output_view: 93 | # Only open the panel when mix is compiling or there is an error. 94 | output_view = create_mix_format_panel(window, panel_name, cmd_args, cwd) 95 | window.run_command('show_panel', panel_params) 96 | 97 | output_view.run_command('append', {'characters': text, 'disable_tab_translation': True}) 98 | except BaseException as e: 99 | write_output(PRINT_PREFIX + " Exception: %s" % repr(e)) 100 | 101 | if output_view: 102 | output_view.set_read_only(True) 103 | failed_msg_region = output_view.find("mix format failed", 0, sublime.IGNORECASE) 104 | failed_msg_region and output_view.show_at_center(failed_msg_region) 105 | 106 | # Either there was no output or there was but without an error. 107 | if not output_view or not failed_msg_region: 108 | if window.active_panel() == panel_params['panel']: 109 | window.run_command('hide_panel', panel_params) 110 | window.destroy_output_panel(panel_name) 111 | 112 | msg = 'Formatted %s %s!' % (file_path and 'file' or 'directory', repr(file_path or cwd)) 113 | print_status_msg(msg) 114 | 115 | def create_mix_format_panel(window, panel_name, cmd_args, cwd): 116 | first_lines = '$ cd %s && %s' % (shlex.quote(cwd), ' '.join(map(shlex.quote, cmd_args))) 117 | first_lines += '\n# Timestamp: %s\n\n' % datetime.now().replace(microsecond=0) 118 | 119 | output_view = window.create_output_panel(panel_name) 120 | output_view.settings().set('result_file_regex', MIX_RESULT_FILE_REGEX) 121 | output_view.settings().set('result_base_dir', cwd) 122 | output_view.set_read_only(False) 123 | output_view.run_command('append', {'characters': first_lines}) 124 | output_view.run_command('move_to', {'to': 'eof'}) 125 | 126 | return output_view 127 | -------------------------------------------------------------------------------- /commands/utils.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import json 3 | from pathlib import Path 4 | 5 | __author__ = 'Aziz Köksal' 6 | __email__ = 'aziz.koeksal@gmail.com' 7 | __status__ = 'Production' 8 | 9 | SETTINGS_FILE_NAME = 'ElixirSyntax.sublime-settings' 10 | 11 | PRINT_PREFIX = 'ElixirSyntax:' 12 | 13 | # The regex is used by Sublime to find and jump to error locations shown in output panels. 14 | MIX_RESULT_FILE_REGEX = r'(\S+?[/\\]\S+?\.[a-zA-Z]+):(\d+)(?::(\d+))?' 15 | 16 | COULDNT_FIND_MIX_EXS = \ 17 | 'Error: could not find a mix.exs file!\n' + \ 18 | 'Make sure that you are in a mix project.' 19 | 20 | def print_status_msg(msg): 21 | print(PRINT_PREFIX, msg) 22 | sublime.status_message(PRINT_PREFIX + ' ' + msg) 23 | 24 | def unique_items(items): 25 | unique_items, seen_items = [], set() 26 | 27 | for item in items: 28 | if item not in seen_items: 29 | unique_items.append(item) 30 | seen_items.add(item) 31 | 32 | return unique_items 33 | 34 | def expand_scope_right(view, begin_point, scope): 35 | end_point = next( 36 | (pt for pt in range(begin_point, view.size()) if not view.match_selector(pt, scope)), 37 | begin_point 38 | ) 39 | return sublime.Region(begin_point, end_point) 40 | 41 | def has_one_of_scope_suffixes(view, scope_suffixes): 42 | view_scope_suffixes = view.scope_name(0).split(' ')[0].split('.')[1:] 43 | return any(suffix in view_scope_suffixes for suffix in scope_suffixes) 44 | 45 | def is_elixir_syntax(view): 46 | return has_one_of_scope_suffixes(view, ['elixir']) 47 | 48 | def is_formattable_syntax(view): 49 | return has_one_of_scope_suffixes(view, ['elixir', 'eex', 'heex', 'surface']) 50 | 51 | def reverse_find_root_folder(bottom_path): 52 | bottom_path = Path(bottom_path) 53 | parent_path = bottom_path.parent if bottom_path.is_file() else bottom_path 54 | 55 | while True: 56 | # We have to check for the root mix.exs, ignoring possible sub-app mix files. 57 | if (parent_path / 'mix.exs').exists() \ 58 | and ( 59 | (parent_path / 'mix.lock').exists() 60 | or (parent_path / '_build').exists() 61 | or parent_path.name != 'apps' and not (parent_path.parent / 'mix.exs').exists() 62 | ): 63 | return str(parent_path) 64 | 65 | old_path, parent_path = parent_path, parent_path.parent 66 | 67 | if old_path == parent_path: 68 | break 69 | 70 | return None 71 | 72 | def read_proc_text_output(proc, size=1024): 73 | while proc.poll() is None: 74 | # TODO: the subprocess should be opened with an encoding to avoid the decode call, 75 | # but the option is not supported in Sublime's Python yet. 76 | text = proc.stdout.read(size).decode(encoding='UTF-8') 77 | if not text: continue 78 | yield text 79 | return '' 80 | 81 | def save_json_file(file_path, dict_data): 82 | try: 83 | with open(str(file_path), 'w') as file: 84 | try: 85 | return json.dump(dict_data, file, indent=2) 86 | except BaseException as e: 87 | print_status_msg('Error: could not save JSON to: %r\nException: %s' % (file_path, e)) 88 | except BaseException as e: 89 | print_status_msg('Error: could not open file: %r\nException: %s' % (file_path, e)) 90 | 91 | def load_json_file(file_path): 92 | try: 93 | with open(str(file_path), 'r') as file: 94 | try: 95 | return json.load(file) 96 | except BaseException as e: 97 | print_status_msg('Error: could not load JSON from: %r\nException: %s' % (file_path, e)) 98 | except BaseException as e: 99 | exists = Path(file_path).exists() 100 | exists and print_status_msg('Error: could not open file: %r\nException: %s' % (file_path, e)) 101 | 102 | return {} 103 | -------------------------------------------------------------------------------- /completions/Phoenix_Attributes.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "text.html.heex meta.attribute-with-value", 3 | "completions": [ 4 | { 5 | "trigger": "phx-value-*", 6 | "contents": "phx-value-${1:*}=\"$2\"", 7 | "kind": "snippet", 8 | "details": "Params" 9 | }, 10 | { 11 | "trigger": "phx-click", 12 | "contents": "phx-click=\"$1\"", 13 | "kind": "snippet", 14 | "details": "Click Events" 15 | }, 16 | { 17 | "trigger": "phx-click-away", 18 | "contents": "phx-click-away=\"$1\"", 19 | "kind": "snippet", 20 | "details": "Click Events" 21 | }, 22 | { 23 | "trigger": "phx-change", 24 | "contents": "phx-change=\"$1\"", 25 | "kind": "snippet", 26 | "details": "Form Events" 27 | }, 28 | { 29 | "trigger": "phx-submit", 30 | "contents": "phx-submit=\"$1\"", 31 | "kind": "snippet", 32 | "details": "Form Events" 33 | }, 34 | { 35 | "trigger": "phx-feedback-for", 36 | "contents": "phx-feedback-for=\"$1\"", 37 | "kind": "snippet", 38 | "details": "Form Events" 39 | }, 40 | { 41 | "trigger": "phx-disable-with", 42 | "contents": "phx-disable-with=\"$1\"", 43 | "kind": "snippet", 44 | "details": "Form Events" 45 | }, 46 | { 47 | "trigger": "phx-trigger-action", 48 | "contents": "phx-trigger-action=\"$1\"", 49 | "kind": "snippet", 50 | "details": "Form Events" 51 | }, 52 | { 53 | "trigger": "phx-auto-recover", 54 | "contents": "phx-auto-recover=\"$1\"", 55 | "kind": "snippet", 56 | "details": "Form Events" 57 | }, 58 | { 59 | "trigger": "phx-blur", 60 | "contents": "phx-blur=\"$1\"", 61 | "kind": "snippet", 62 | "details": "Focus Events" 63 | }, 64 | { 65 | "trigger": "phx-focus", 66 | "contents": "phx-focus=\"$1\"", 67 | "kind": "snippet", 68 | "details": "Focus Events" 69 | }, 70 | { 71 | "trigger": "phx-window-blur", 72 | "contents": "phx-window-blur=\"$1\"", 73 | "kind": "snippet", 74 | "details": "Focus Events" 75 | }, 76 | { 77 | "trigger": "phx-window-focus", 78 | "contents": "phx-window-focus=\"$1\"", 79 | "kind": "snippet", 80 | "details": "Focus Events" 81 | }, 82 | { 83 | "trigger": "phx-keydown", 84 | "contents": "phx-keydown=\"$1\"", 85 | "kind": "snippet", 86 | "details": "Key Events" 87 | }, 88 | { 89 | "trigger": "phx-keyup", 90 | "contents": "phx-keyup=\"$1\"", 91 | "kind": "snippet", 92 | "details": "Key Events" 93 | }, 94 | { 95 | "trigger": "phx-window-keydown", 96 | "contents": "phx-window-keydown=\"$1\"", 97 | "kind": "snippet", 98 | "details": "Key Events" 99 | }, 100 | { 101 | "trigger": "phx-window-keyup", 102 | "contents": "phx-window-keyup=\"$1\"", 103 | "kind": "snippet", 104 | "details": "Key Events" 105 | }, 106 | { 107 | "trigger": "phx-key", 108 | "contents": "phx-key=\"$1\"", 109 | "kind": "snippet", 110 | "details": "Key Events" 111 | }, 112 | { 113 | "trigger": "phx-update", 114 | "contents": "phx-update=\"$1\"", 115 | "kind": "snippet", 116 | "details": "DOM Patching" 117 | }, 118 | { 119 | "trigger": "phx-remove", 120 | "contents": "phx-remove=\"$1\"", 121 | "kind": "snippet", 122 | "details": "DOM Patching" 123 | }, 124 | { 125 | "trigger": "phx-hook", 126 | "contents": "phx-hook=\"$1\"", 127 | "kind": "snippet", 128 | "details": "JS Interop" 129 | }, 130 | { 131 | "trigger": "phx-debounce", 132 | "contents": "phx-debounce=\"$1\"", 133 | "kind": "snippet", 134 | "details": "Rate Limiting" 135 | }, 136 | { 137 | "trigger": "phx-throttle", 138 | "contents": "phx-throttle=\"$1\"", 139 | "kind": "snippet", 140 | "details": "Rate Limiting" 141 | }, 142 | { 143 | "trigger": "phx-track-static", 144 | "contents": "phx-track-static=\"$1\"", 145 | "kind": "snippet", 146 | "details": "Static Tracking" 147 | } 148 | ] 149 | } 150 | -------------------------------------------------------------------------------- /completions/Surface_Attributes.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "text.html.surface meta.attribute-with-value", 3 | "completions": [ 4 | { 5 | "trigger": "if", 6 | "contents": ":if={$1}", 7 | "kind": "markup", 8 | "details": ":if" 9 | }, 10 | { 11 | "trigger": "hook", 12 | "contents": ":hook={$1}", 13 | "kind": "markup", 14 | "details": ":hook" 15 | }, 16 | { 17 | "trigger": "show", 18 | "contents": ":show={$1}", 19 | "kind": "markup", 20 | "details": ":show" 21 | }, 22 | { 23 | "trigger": "let", 24 | "contents": ":let={$1}", 25 | "kind": "markup", 26 | "details": ":let" 27 | }, 28 | { 29 | "trigger": "args", 30 | "contents": ":args={$1}", 31 | "kind": "markup", 32 | "details": ":args" 33 | }, 34 | { 35 | "trigger": "values", 36 | "contents": ":values={$1}", 37 | "kind": "markup", 38 | "details": ":values" 39 | }, 40 | { 41 | "trigger": "on-click", 42 | "contents": ":on-click={$1}", 43 | "kind": "markup", 44 | "details": ":on-click" 45 | }, 46 | { 47 | "trigger": "on-capture-click", 48 | "contents": ":on-capture-click={$1}", 49 | "kind": "markup", 50 | "details": ":on-capture-click" 51 | }, 52 | { 53 | "trigger": "on-blur", 54 | "contents": ":on-blur={$1}", 55 | "kind": "markup", 56 | "details": ":on-blur" 57 | }, 58 | { 59 | "trigger": "on-focus", 60 | "contents": ":on-focus={$1}", 61 | "kind": "markup", 62 | "details": ":on-focus" 63 | }, 64 | { 65 | "trigger": "on-change", 66 | "contents": ":on-change={$1}", 67 | "kind": "markup", 68 | "details": ":on-change" 69 | }, 70 | { 71 | "trigger": "on-submit", 72 | "contents": ":on-submit={$1}", 73 | "kind": "markup", 74 | "details": ":on-submit" 75 | }, 76 | { 77 | "trigger": "on-keydown", 78 | "contents": ":on-keydown={$1}", 79 | "kind": "markup", 80 | "details": ":on-keydown" 81 | }, 82 | { 83 | "trigger": "on-keyup", 84 | "contents": ":on-keyup={$1}", 85 | "kind": "markup", 86 | "details": ":on-keyup" 87 | }, 88 | { 89 | "trigger": "on-window-focus", 90 | "contents": ":on-window-focus={$1}", 91 | "kind": "markup", 92 | "details": ":on-window-focus" 93 | }, 94 | { 95 | "trigger": "on-window-blur", 96 | "contents": ":on-window-blur={$1}", 97 | "kind": "markup", 98 | "details": ":on-window-blur" 99 | }, 100 | { 101 | "trigger": "on-window-keydown", 102 | "contents": ":on-window-keydown={$1}", 103 | "kind": "markup", 104 | "details": ":on-window-keydown" 105 | }, 106 | { 107 | "trigger": "on-window-keyup", 108 | "contents": ":on-window-keyup={$1}", 109 | "kind": "markup", 110 | "details": ":on-window-keyup" 111 | } 112 | ] 113 | } 114 | -------------------------------------------------------------------------------- /dependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "*": { 3 | "*": [ 4 | "pathlib" 5 | ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /images/elixir_fragment_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 |
    Post 21 | |> where([post], fragment("?.full_text <@ to_tsquery(?)", post, ^terms)) 22 |
    23 |
    24 |
    25 | -------------------------------------------------------------------------------- /images/elixir_heex_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 |
    ~H"<.form><%= @input %></.form>" 21 |
    22 |
    23 |
    24 | -------------------------------------------------------------------------------- /images/elixir_json_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 |
    ~j""" 22 | { 23 | "key": "#{value}", 24 | "#{key}": "value" 25 | } 26 | """ 27 |
    28 |
    29 |
    30 | -------------------------------------------------------------------------------- /images/elixir_liveview_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 |
    ~L"<div><%= @live! %></div>" 21 |
    22 |
    23 |
    24 | -------------------------------------------------------------------------------- /images/elixir_regex_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 |
    ~r"(?>[[:alpha:]_][\w@]*+[?!]?+)" 22 |
    23 |
    24 |
    25 | -------------------------------------------------------------------------------- /images/elixir_sql_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 |
    defmacro sql(sql), do: sql 21 | sql("SELECT * FROM posts ORDER BY title GROUP BY user_id") 22 |
    23 |
    24 |
    25 | -------------------------------------------------------------------------------- /images/elixir_surface_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 |
    ~F"<Face {=@type}></Face>" 21 |
    22 |
    23 |
    24 | -------------------------------------------------------------------------------- /images/elixir_type_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 |
    @type get_fun(data) :: (:get, data, (term -> term) -> new_data :: container) 23 |
    24 |
    25 |
    26 | -------------------------------------------------------------------------------- /images/elixir_yaml_example.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 |
    ~Y""" 22 | user: 23 | name: YAML 24 | born: 2001-05-11 25 | """ 26 |
    27 |
    28 |
    29 | -------------------------------------------------------------------------------- /keymaps/Default.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | // Runs the whole test-suite. 3 | { "keys": ["ctrl+alt+shift+t"], "command": "mix_test" }, 4 | // Runs the current test file. 5 | { "keys": ["ctrl+alt+t"], "command": "mix_test_file" }, 6 | // Runs the current test file but including only the tests marked by the selected lines. 7 | { "keys": ["alt+shift+t"], "command": "mix_test_selection" }, 8 | // Runs the previously executed command. 9 | { "keys": ["alt+shift+r"], "command": "mix_test_repeat" }, 10 | // Formats the source file. 11 | { "keys": ["alt+shift+f"], "command": "mix_format_file", 12 | "context": [ { "key": "selector", "operator": "equal", "operand": "source.elixir | source.elixir.eex | text.eex | text.html.eex | text.html.heex | text.html.surface" } ] 13 | }, 14 | ] 15 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from .commands import * 2 | -------------------------------------------------------------------------------- /menus/Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "preferences", 4 | "children": [ 5 | { 6 | "caption": "Package Settings", 7 | "mnemonic": "P", 8 | "id": "package-settings", 9 | "children": [ 10 | { 11 | "caption": "ElixirSyntax", 12 | "children": [ 13 | { 14 | "caption": "README", 15 | "command": "open_file", 16 | "args": { 17 | "file": "${packages}/ElixirSyntax/README.md" 18 | } 19 | }, 20 | { 21 | "caption": "CHANGELOG", 22 | "command": "open_file", 23 | "args": { 24 | "file": "${packages}/ElixirSyntax/CHANGELOG.md" 25 | } 26 | }, 27 | { "caption": "-" }, 28 | { 29 | "caption": "Settings", 30 | "command": "edit_settings", 31 | "args": { 32 | "base_file": "${packages}/ElixirSyntax/settings/ElixirSyntax.sublime-settings", 33 | "default": "{\n $0\n}\n" 34 | } 35 | }, 36 | { 37 | "caption": "Key Bindings", 38 | "command": "edit_settings", 39 | "args": { 40 | "base_file": "${packages}/ElixirSyntax/keymaps/Default.sublime-keymap", 41 | "default": "[\n $0\n]\n" 42 | } 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /preferences/Attributes.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | Attributes 6 | scope 7 | entity.name.constant.elixir 8 | settings 9 | 10 | showInIndexedSymbolList 11 | 1 12 | showInSymbolList 13 | 1 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /preferences/EEx_Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | EEx Comments 6 | scope 7 | text.eex | text.html.eex | text.html.heex 8 | settings 9 | 10 | shellVariables 11 | 12 | 13 | name 14 | TM_COMMENT_START 15 | value 16 | 17 | 18 | 19 | name 20 | TM_COMMENT_END 21 | value 22 | ]]> 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /preferences/Elixir_Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | Elixir Comments 6 | scope 7 | source.elixir 8 | settings 9 | 10 | shellVariables 11 | 12 | 13 | name 14 | TM_COMMENT_START 15 | value 16 | # 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /preferences/Fold.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | source.elixir 6 | settings 7 | 8 | foldScopes 9 | 10 | begin 11 | meta.doc punctuation.definition.string.begin 12 | end 13 | meta.doc punctuation.definition.string.end 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /preferences/Indentation_Rules.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | source.elixir 6 | settings 7 | 8 | increaseIndentPattern 9 | ^\s*(after|else|catch|rescue|^.*\b(do|<-|->|[{\[(]))\b(?![?!:])\s*$ 10 | decreaseIndentPattern 11 | ^\s*([}\])]\s*$|(after|else|catch|rescue|end)\b(?![?!:])) 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /preferences/Surface_Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | name 5 | Surface Comments 6 | scope 7 | text.html.surface 8 | settings 9 | 10 | shellVariables 11 | 12 | 13 | name 14 | TM_COMMENT_START 15 | value 16 | 17 | 18 | 19 | name 20 | TM_COMMENT_END 21 | value 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /scripts/count_tokens_by_scope.py: -------------------------------------------------------------------------------- 1 | # This script can be used in Sublime's Python console 2 | # to determine the count of certain scopes or tokens. 3 | from itertools import groupby 4 | scope_select = "variable.function.built-in.elixir" 5 | sorted(list((len(list(g)), k) for k, g in groupby(sorted([view.substr(x) for x in view.find_by_selector(scope_select)])))) 6 | -------------------------------------------------------------------------------- /scripts/print_html.py: -------------------------------------------------------------------------------- 1 | # This script can be used in Sublime's Python console. 2 | 3 | # Converts the current file to HTML (simple span tags with inline styles.) 4 | view.export_to_html() 5 | 6 | # Converts the current selection to HTML. 7 | view.export_to_html(view.sel()) 8 | -------------------------------------------------------------------------------- /settings/ElixirSyntax.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "mix_test": { 3 | // Which command to execute. 4 | "cmd": ["mix", "test"], 5 | // Choose where to display the command's output. 6 | // `{"values": ["tab", "panel", "file://..."]}` 7 | "output": "panel", 8 | // Output mode of the disk file to open/create. 9 | // `{"values": "see `open()` modifiers"}` 10 | "output_mode": "w", 11 | // Additional arguments to pass to `cmd`. 12 | // `{"values": "see `mix help test`"}` 13 | "args": [], 14 | // The seed with which to randomize the tests. 15 | // `{"values": [null, "non-negative integer"]}` 16 | "seed": null 17 | }, 18 | "mix_format": { 19 | // Which command to execute. 20 | "cmd": ["mix", "format"], 21 | // Calls `mix format` automatically on saving the file if `true`. 22 | "on_save": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /snippets/EEx_Code.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | ]]> 3 | <% 4 | text.eex | text.html.eex | text.html.heex | source.elixir.eex 5 | EEx code <% ... %> 6 | 7 | -------------------------------------------------------------------------------- /snippets/EEx_Comment.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | ]]> 3 | <%!-- 4 | text.eex | text.html.eex | text.html.heex | source.elixir.eex 5 | EEx comment <%!-- ... --%> 6 | 7 | -------------------------------------------------------------------------------- /snippets/EEx_Interpolation.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | ]]> 3 | <%= 4 | text.eex | text.html.eex | text.html.heex | source.elixir.eex 5 | EEx interpolation <%= ... %> 6 | 7 | -------------------------------------------------------------------------------- /snippets/EEx_Raw.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | ]]> 3 | <%% 4 | text.eex | text.html.eex | text.html.heex | source.elixir.eex 5 | EEx raw <%% ... %> 6 | 7 | -------------------------------------------------------------------------------- /snippets/Elixir_Interpolation.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 4 | meta.string.elixir - string.quoted.other.literal 5 | Elixir code interpolation. 6 | 7 | -------------------------------------------------------------------------------- /snippets/IEx.pry.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | pry 4 | source.elixir 5 | Pries into the process environment for debugging. 6 | 7 | -------------------------------------------------------------------------------- /snippets/IO_inspect.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | ii 4 | source.elixir 5 | Print to stdout 6 | 7 | -------------------------------------------------------------------------------- /snippets/IO_inspect_label.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | IO.inspect(label: "${2:$TM_FILENAME:$TM_LINE_NUMBER: }${1:...}")]]> 3 | iil 4 | source.elixir 5 | Print to stdout with label 6 | 7 | -------------------------------------------------------------------------------- /snippets/Kernel.dbg.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | dbg($1)]]> 3 | dbg 4 | source.elixir 5 | Debugs the given `code`. Returns value unchanged. 6 | 7 | -------------------------------------------------------------------------------- /snippets/Kernel.tap&.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | tap(& ${1:&1})]]> 3 | tap& 4 | source.elixir 5 | Pipes into `&`. Returns value unchanged. 6 | 7 | -------------------------------------------------------------------------------- /snippets/Kernel.tap.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | tap(fn ${1:x} -> ${2:x} end)]]> 3 | tap 4 | source.elixir 5 | Pipes into `fn`. Returns value unchanged. 6 | 7 | -------------------------------------------------------------------------------- /snippets/Kernel.then&.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | then(& ${1:&1})]]> 3 | then& 4 | source.elixir 5 | Pipes into `&`. 6 | 7 | -------------------------------------------------------------------------------- /snippets/Kernel.then.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | then(fn ${1:x} -> ${2:x} end)]]> 3 | then 4 | source.elixir 5 | Pipes into `fn` 6 | 7 | -------------------------------------------------------------------------------- /syntaxes/EEx.sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | version: 2 4 | name: EEx 5 | scope: text.eex 6 | 7 | file_extensions: [eex] 8 | authors: [Aziz Köksal ] 9 | 10 | contexts: 11 | # EEx: 12 | main: 13 | - include: eex-embedded 14 | 15 | eex-interpolated-comment: 16 | - match: (<%)(!--) 17 | captures: 18 | 0: comment.block.eex 19 | 1: punctuation.section.embedded.begin.eex 20 | 2: punctuation.definition.comment.begin.eex 21 | push: 22 | - eex-interpolated-clear-scope-pop 23 | - eex-embedded-block-comment-content-pop 24 | - match: (<%)(#) 25 | captures: 26 | 0: comment.block.eex 27 | 1: punctuation.section.embedded.begin.eex 28 | 2: punctuation.definition.comment.begin.eex 29 | push: 30 | - eex-interpolated-clear-scope-pop 31 | - eex-embedded-comment-content-deprecated-pop 32 | 33 | eex-interpolated-clear-scope-pop: 34 | # Need to clear the string scope so that auto-completion works, 35 | # which is disabled by default in strings and comments. 36 | - clear_scopes: 1 37 | - match: '' 38 | pop: 1 39 | 40 | eex-embedded-comment: 41 | - match: (<%)(!--) 42 | captures: 43 | 0: comment.block.eex 44 | 1: punctuation.section.embedded.begin.eex 45 | 2: punctuation.definition.comment.begin.eex 46 | push: eex-embedded-block-comment-content-pop 47 | - match: (<%)(#) 48 | captures: 49 | 0: comment.block.eex 50 | 1: punctuation.section.embedded.begin.eex 51 | 2: punctuation.definition.comment.begin.eex 52 | push: eex-embedded-comment-content-deprecated-pop 53 | 54 | eex-embedded-block-comment-content-pop: 55 | - meta_scope: meta.embedded.eex 56 | - meta_content_scope: comment.block.eex 57 | - include: eex-comment-end-pop 58 | 59 | eex-embedded-comment-content-deprecated-pop: 60 | - meta_scope: meta.embedded.eex meta.deprecated.eex 61 | - meta_content_scope: comment.block.eex 62 | - include: eex-comment-end-deprecated-pop 63 | 64 | eex-embedded: 65 | - include: eex-embedded-comment 66 | 67 | # Tags <%/ and <%| are parsed but have no functionality in EEx yet. 68 | - match: <%(?:%=?|[=/|]?) 69 | scope: punctuation.section.embedded.begin.eex 70 | push: eex-embedded-content-pop 71 | 72 | eex-embedded-content-pop: 73 | - meta_scope: meta.embedded.eex 74 | - include: eex-tag-end-pop 75 | - include: elixir-line-comment 76 | - include: scope:source.elixir 77 | apply_prototype: true 78 | 79 | elixir-line-comment: 80 | - match: \# 81 | scope: punctuation.definition.comment.elixir 82 | push: 83 | - meta_scope: comment.line.number-sign.elixir 84 | - match: \n|(?=%>) 85 | pop: 1 86 | 87 | eex-tag-end-pop: 88 | - match: '%>' 89 | scope: punctuation.section.embedded.end.eex 90 | pop: 1 91 | 92 | eex-comment-end-pop: 93 | - match: (--)(%>) 94 | captures: 95 | 0: comment.block.eex 96 | 1: punctuation.definition.comment.end.eex 97 | 2: punctuation.section.embedded.end.eex 98 | pop: 1 99 | 100 | eex-comment-end-deprecated-pop: 101 | - match: '%>' 102 | scope: comment.block.eex punctuation.section.embedded.end.eex 103 | pop: 1 104 | -------------------------------------------------------------------------------- /syntaxes/Elixir (EEx).sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | version: 2 4 | name: Elixir (EEx) 5 | scope: source.elixir.eex 6 | extends: Elixir.sublime-syntax 7 | 8 | file_extensions: [ex.eex, exs.eex] 9 | first_line_match: ^#\s*exs?\.eex 10 | authors: [Aziz Köksal ] 11 | 12 | contexts: 13 | # Use prototype to work around the "context sanity limit" error. 14 | # An issue that remains is that "<% ... %>" embeds are not independent 15 | # of the surroundings. 16 | prototype: 17 | - include: eex_begin_tag 18 | - include: eex_end_tag 19 | 20 | eex_begin_tag: 21 | - include: scope:text.eex#eex-interpolated-comment 22 | 23 | - match: <%(?:%=?|[=/|]?) 24 | scope: punctuation.section.embedded.begin.eex 25 | # # NB: causes "context sanity limit" error. 26 | # push: core_syntax 27 | # with_prototype: 28 | # - match: (?=%>|<%) 29 | # pop: 1 30 | 31 | eex_end_tag: 32 | - match: \s*(%>) 33 | captures: 34 | 1: punctuation.section.embedded.end.eex 35 | -------------------------------------------------------------------------------- /syntaxes/HTML (EEx).sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | version: 2 4 | name: HTML (EEx) 5 | scope: text.html.eex 6 | extends: Packages/HTML/HTML.sublime-syntax 7 | 8 | file_extensions: [html.eex, html.leex] 9 | authors: 10 | - Aziz Köksal 11 | - deathaxe 12 | 13 | contexts: 14 | # HTML: 15 | 16 | prototype: 17 | - meta_prepend: true 18 | - include: eex-embedded 19 | 20 | tag-html: 21 | - meta_append: true 22 | - include: eex-embedded-tag-name 23 | 24 | tag-attributes: 25 | - meta_prepend: true 26 | - include: eex-embedded-attribute-name 27 | 28 | tag-attribute-value-content: 29 | - meta_prepend: true 30 | - include: eex-interpolations 31 | 32 | strings-common-content: 33 | - meta_prepend: true 34 | - include: eex-interpolations 35 | 36 | # EEx: 37 | 38 | eex-interpolations: 39 | - include: scope:text.eex#eex-interpolated-comment 40 | # Tags <%/ and <%| are parsed but have no functionality in EEx yet. 41 | - match: <%(?:%=?|[=/|]?) 42 | scope: punctuation.section.embedded.begin.eex 43 | push: eex-interpolation-tag-content-pop 44 | 45 | eex-interpolation-tag-content-pop: 46 | # Need to clear the string scope. 47 | - clear_scopes: 1 48 | - meta_include_prototype: false 49 | - meta_scope: meta.embedded.eex 50 | - meta_content_scope: source.elixir.embedded.html 51 | - include: eex-embedded-content-pop 52 | 53 | eex-embedded: 54 | - include: scope:text.eex#eex-embedded-comment 55 | # Tags <%/ and <%| are parsed but have no functionality in EEx yet. 56 | - match: <%(?:%=?|[=/|]?) 57 | scope: punctuation.section.embedded.begin.eex 58 | push: eex-embedded-content-pop 59 | 60 | eex-embedded-content-pop: 61 | - meta_include_prototype: false 62 | - meta_scope: meta.embedded.eex 63 | - meta_content_scope: source.elixir.embedded.html 64 | - include: scope:text.eex#eex-tag-end-pop 65 | - include: scope:text.eex#elixir-line-comment 66 | - include: scope:source.elixir 67 | apply_prototype: true 68 | 69 | eex-embedded-tag-name: 70 | - match: ( 11 | - deathaxe 12 | 13 | variables: 14 | heex_tag_char: '[^ \n."''/=<>\x{7F}-\x{9F}]' 15 | heex_tag_name: '[A-Z]{{heex_tag_char}}*' 16 | heex_slot_name: ':[a-zA-Z]{{heex_tag_char}}*' 17 | is_heex_tag_name: (?=[A-Z]|\.[a-z]|:[a-zA-Z]) 18 | 19 | contexts: 20 | # HTML overrides: 21 | 22 | tag-attributes: 23 | - meta_prepend: true 24 | - include: heex-phx-attributes 25 | - include: heex-special-attributes 26 | - include: elixir-embedded 27 | - match: = 28 | scope: invalid.attribute.heex punctuation.separator.key-value.html 29 | push: [tag-generic-attribute-meta, tag-generic-attribute-value] 30 | 31 | tag-other: 32 | - meta_prepend: true 33 | - include: heex-tags 34 | - include: HTML (EEx).sublime-syntax#eex-embedded 35 | 36 | tag-generic-attribute-value: 37 | - meta_prepend: true 38 | - include: elixir-embedded-pop 39 | 40 | tag-class-attribute-value: 41 | - meta_prepend: true 42 | - include: elixir-embedded-pop 43 | 44 | tag-event-attribute-value: 45 | - meta_prepend: true 46 | - include: elixir-embedded-pop 47 | 48 | tag-href-attribute-value: 49 | - meta_prepend: true 50 | - include: elixir-embedded-pop 51 | 52 | tag-id-attribute-value: 53 | - meta_prepend: true 54 | - include: elixir-embedded-pop 55 | 56 | tag-style-attribute-value: 57 | - meta_prepend: true 58 | - include: elixir-embedded-pop 59 | 60 | # HEEx: 61 | 62 | heex-tags: 63 | - match: <{{is_heex_tag_name}} 64 | scope: punctuation.definition.tag.begin.html 65 | push: [heex-begin-tag-content-pop, heex-begin-tag-name-pop] 66 | - match: 102 | value-.+? 103 | | click(?:-away)? 104 | | change | submit | feedback-for | disable-with | trigger-action | auto-recover 105 | | blur | focus | window(?:-blur|-focus|-keydown|-keyup) 106 | | key(?:down|up)? 107 | | update | remove 108 | | hook 109 | | debounce | throttle 110 | | track-static 111 | ){{attribute_name_break}} 112 | scope: entity.other.attribute-name.heex 113 | push: [tag-generic-attribute-meta, tag-generic-attribute-assignment] 114 | 115 | heex-special-attributes: 116 | - match: (:)(if|let)(?=={) 117 | captures: 118 | 2: entity.other.attribute-name.heex 119 | push: [tag-generic-attribute-meta, tag-generic-attribute-assignment] 120 | - match: (:)(for)(?=={) 121 | captures: 122 | 2: entity.other.attribute-name.heex 123 | push: [tag-generic-attribute-meta, heex-for-attribute-pop] 124 | 125 | heex-for-attribute-pop: 126 | - match: (=)(\{) 127 | captures: 128 | 1: punctuation.separator.key-value.html 129 | 2: punctuation.section.embedded.begin.elixir 130 | set: 131 | - meta_scope: meta.embedded.heex 132 | - meta_content_scope: source.elixir.embedded.html 133 | - match: \} 134 | scope: punctuation.section.embedded.end.elixir 135 | pop: 1 136 | - include: scope:source.elixir#one_arrow_clause_or_argument 137 | apply_prototype: true 138 | 139 | # Elixir: 140 | 141 | elixir-embedded: 142 | - match: (?={) 143 | push: elixir-embedded-pop 144 | 145 | elixir-embedded-pop: 146 | - match: \{ 147 | scope: punctuation.section.embedded.begin.elixir 148 | set: elixir-embedded-content-pop 149 | 150 | elixir-embedded-content-pop: 151 | - meta_scope: meta.embedded.heex 152 | - meta_content_scope: source.elixir.embedded.html 153 | - include: elixir-embedded-content-nometa-pop 154 | 155 | elixir-embedded-content-nometa-pop: 156 | - match: \} 157 | scope: punctuation.section.embedded.end.elixir 158 | pop: 1 159 | - include: scope:source.elixir 160 | apply_prototype: true 161 | -------------------------------------------------------------------------------- /syntaxes/HTML (Surface).sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | version: 2 4 | name: HTML (Surface) 5 | scope: text.html.surface 6 | extends: Packages/HTML/HTML.sublime-syntax 7 | 8 | file_extensions: [sface] 9 | authors: 10 | - Aziz Köksal 11 | - deathaxe 12 | 13 | variables: 14 | html_custom_tag_char: '[^ \n"''/=<>\x{7F}-\x{9F}]' 15 | surface_tag_char: '[^ \n."''/=<>\x{7F}-\x{9F}]' 16 | surface_tag_name: '[a-zA-Z]{{surface_tag_char}}*' 17 | surface_upcase_tag_name: '[A-Z]{{surface_tag_char}}*' 18 | 19 | contexts: 20 | # HTML overrides: 21 | 22 | tag-other: 23 | - include: surface-markdown-tag 24 | - include: surface-raw-tag 25 | - include: surface-other-tags 26 | - include: html-custom-tags 27 | - include: surface-blocks 28 | - include: surface-comment 29 | - include: elixir-embedded 30 | 31 | tag-attributes: 32 | - meta_prepend: true 33 | - include: surface-attributes 34 | - include: elixir-embedded 35 | 36 | tag-generic-attribute-value: 37 | - meta_prepend: true 38 | - include: elixir-embedded-pop 39 | 40 | tag-class-attribute-value: 41 | - meta_prepend: true 42 | - include: elixir-embedded-pop 43 | 44 | tag-event-attribute-value: 45 | - meta_prepend: true 46 | - include: elixir-embedded-pop 47 | 48 | tag-href-attribute-value: 49 | - meta_prepend: true 50 | - include: elixir-embedded-pop 51 | 52 | tag-id-attribute-value: 53 | - meta_prepend: true 54 | - include: elixir-embedded-pop 55 | 56 | tag-style-attribute-value: 57 | - meta_prepend: true 58 | - include: elixir-embedded-pop 59 | 60 | # Surface: 61 | 62 | surface-markdown-tag: 63 | # NB: the #Markdown-tag cannot be nested, e.g.: `<#Markdown><#Markdown>` 64 | - match: (<)(#Markdown)\b 65 | captures: 66 | 1: punctuation.definition.tag.begin.html 67 | 2: entity.name.tag.begin.surface 68 | push: surface-markdown-tag-content-pop 69 | 70 | surface-markdown-tag-content-pop: 71 | - meta_scope: meta.tag.other.surface 72 | - match: \> 73 | scope: punctuation.definition.tag.end.html 74 | set: surface-markdown-body-pop 75 | - include: tag-end-self-closing 76 | - include: tag-attributes 77 | 78 | surface-markdown-body-pop: 79 | - match: (<#Raw>` 97 | - match: (<)(#Raw)(>) 98 | captures: 99 | 0: meta.tag.other.surface 100 | 1: punctuation.definition.tag.begin.html 101 | 2: entity.name.tag.begin.surface 102 | 3: punctuation.definition.tag.end.html 103 | push: surface-raw-body-pop 104 | 105 | surface-raw-body-pop: 106 | - match: (] 8 | 9 | contexts: 10 | main: 11 | - match: | 12 | (?xi) 13 | (?> 14 | ( 15 | # Scalar values. 16 | current_(?>catalog|role|schema) | localtime(?:stamp)? 17 | ) 18 | | ( 19 | # Should always be keywords, but SQL.sublime-syntax doesn't think so yet. 20 | # Taken from https://www.postgresql.org/docs/current/sql-keywords-appendix.html 21 | all|any|array|analy[sz]e|a?symmetric|authorization|at(?=\s+time\s+zone\b)|binary|both|by 22 | | (?<=\bat\s)time(?=\s+zone\b) | (?<=\btime\s)zone | (?<=\bdo\s)nothing | (?<=\bon\s)conflict | (?<=\bwith\s)ordinality 23 | | cast|cross|column|concurrently|collat(?:e|ion)|create|(?<=\bcreate\s)type|distinct|(?>|[%^@|&#~]) 33 | # Postgres type cast identifier. 34 | | (?<=::)\s*((?!\d)\w+(?:\[\])*) 35 | # Postgres jsonb operators. 36 | | (\\\\)(\?[|&]?)? | (->>?|\#>>?|@[@>?]|<@|\#-) 37 | # Ecto argument placeholder. 38 | | ((?>>: :>>>, ~~~: :~~~, <~>: :<~>, <~: :<~, <<~: :<<~, ~>: :~>, 128 | # ^^^ constant.other.symbol 129 | # ^^^ constant.other.keyword 130 | # ^^^^ constant.other.symbol 131 | # ^^^^ constant.other.keyword 132 | # ^^^ constant.other.symbol 133 | # ^^^ constant.other.keyword 134 | # ^^^^ constant.other.symbol 135 | # ^^^^ constant.other.keyword 136 | # ^^^^ constant.other.symbol 137 | # ^^^^ constant.other.keyword 138 | # ^^^^ constant.other.symbol 139 | # ^^^^ constant.other.keyword 140 | # ^^^^ constant.other.symbol 141 | # ^^^^ constant.other.keyword 142 | ~>>: :~>>, |>: :|>, <|>: :<|>, /: :/, //: ://, \\: :\\, *: :*, **: :**, .: :., ..: :.., ...: :..., 143 | # ^^^^ constant.other.symbol 144 | # ^^^^ constant.other.keyword 145 | # ^^^ constant.other.symbol 146 | # ^^^ constant.other.keyword 147 | # ^^ constant.other.symbol 148 | # ^^ constant.other.keyword 149 | # ^^^ constant.other.symbol 150 | # ^^^ constant.other.keyword 151 | # ^^ constant.other.symbol 152 | # ^^ constant.other.keyword 153 | # ^^^ constant.other.symbol 154 | # ^^^ constant.other.keyword 155 | # ^^^ constant.other.symbol 156 | # ^^^ constant.other.keyword 157 | # ^^ constant.other.symbol 158 | # ^^ constant.other.keyword 159 | # ^^^^ constant.other.symbol 160 | # ^^^^ constant.other.keyword 161 | # ^^^ constant.other.symbol 162 | # ^^^ constant.other.keyword 163 | # ^^^^ constant.other.symbol 164 | # ^^^^ constant.other.keyword 165 | >=: :>=, <=: :<=, <: :<, <-: :<-, <>: :<>, ->: :->, >: :>, 166 | # ^^ constant.other.symbol 167 | # ^^ constant.other.keyword 168 | # ^^^ constant.other.symbol 169 | # ^^^ constant.other.keyword 170 | # ^ punctuation.separator.sequence -invalid 171 | # ^^^ constant.other.symbol 172 | # ^^^ constant.other.keyword 173 | # ^^^ constant.other.symbol 174 | # ^^^ constant.other.keyword 175 | # ^^ constant.other.symbol 176 | # ^^ constant.other.keyword 177 | # ^^^ constant.other.symbol 178 | # ^^^ constant.other.keyword 179 | # ^^^ constant.other.symbol 180 | # ^^^ constant.other.keyword 181 | -: :-, --: :--, ---: :---, +: :+, ++: :++, +++: :+++, 182 | # ^^^^ constant.other.symbol 183 | # ^^^^ constant.other.keyword 184 | # ^^^ constant.other.symbol 185 | # ^^^ constant.other.keyword 186 | # ^^ constant.other.symbol 187 | # ^^ constant.other.keyword 188 | # ^^^^ constant.other.symbol 189 | # ^^^^ constant.other.keyword 190 | # ^^^ constant.other.symbol 191 | # ^^^ constant.other.keyword 192 | # ^^ constant.other.symbol 193 | # ^^ constant.other.keyword 194 | &: :&, &&: :&&, &&&: :&&&, |: :|, ||: :||, |||: :|||, @: :@, 195 | # ^^ constant.other.symbol 196 | # ^^ constant.other.keyword 197 | # ^^^^ constant.other.symbol 198 | # ^^^^ constant.other.keyword 199 | # ^^^ constant.other.symbol 200 | # ^^^ constant.other.keyword 201 | # ^^ constant.other.symbol 202 | # ^^ constant.other.keyword 203 | # ^^^^ constant.other.symbol 204 | # ^^^^ constant.other.keyword 205 | # ^^^ constant.other.symbol 206 | # ^^^ constant.other.keyword 207 | # ^^ constant.other.symbol 208 | # ^^ constant.other.keyword 209 | ^: :^, ^^^: :^^^, "::": :::, 210 | # ^^^ constant.other.symbol 211 | # ^^^^^ constant.other.keyword 212 | # ^^^^ constant.other.symbol 213 | # ^^^^ constant.other.keyword 214 | # ^^ constant.other.symbol 215 | # ^^ constant.other.keyword 216 | 217 | # Special symbols: 218 | <<>>: :<<>>, ..//: :..//, {}: :{}, %{}: :%{}, %: :% 219 | # ^^ constant.other.symbol 220 | # ^^ constant.other.keyword 221 | # ^^^^ constant.other.symbol 222 | # ^^^ constant.other.keyword 223 | # ^^^ constant.other.symbol 224 | # ^^^ constant.other.keyword 225 | # ^^^^^ constant.other.symbol 226 | # ^^^^^ constant.other.keyword 227 | # ^^^^^ constant.other.symbol 228 | # ^^^^^ constant.other.keyword 229 | ] 230 | 231 | 232 | ### Special form variables 233 | 234 | [ 235 | __MODULE__: :__MODULE__, __ENV__: :__ENV__, __DIR__: :__DIR__, 236 | # ^^^^^^^^ constant.other.symbol 237 | # ^^^^^^^^ constant.other.keyword 238 | # ^^^^^^^^ constant.other.symbol 239 | # ^^^^^^^^ constant.other.keyword 240 | # ^^^^^^^^^^^ constant.other.symbol 241 | # ^^^^^^^^^^^ constant.other.keyword 242 | __CALLER__: :__CALLER__, __STACKTRACE__: :__STACKTRACE__, 243 | # ^^^^^^^^^^^^^^^ constant.other.symbol 244 | # ^^^^^^^^^^^^^^^ constant.other.keyword 245 | # ^^^^^^^^^^^ constant.other.symbol 246 | # ^^^^^^^^^^^ constant.other.keyword 247 | ] 248 | 249 | 250 | ### Atom symbol exceptions: 251 | 252 | [ 253 | ?: :?, 254 | # ^^ constant.numeric 255 | # ^ -constant.other.symbol 256 | # ^ constant.other.symbol 257 | # ^^ constant.numeric -constant.other.keyword 258 | :::, 259 | # ^ punctuation.separator.sequence 260 | # ^^^ constant.other.symbol 261 | :: ::, 262 | # ^^ keyword.operator.colon 263 | # ^^ keyword.operator.colon 264 | ^^: :^^, 265 | # ^ keyword.operator.pin 266 | # ^^ constant.other.symbol 267 | # ^^ constant.other.keyword 268 | # ^ -constant.other.keyword 269 | []: :[], 270 | # ^^ -constant.other.symbol 271 | # ^ constant.other.symbol 272 | # ^ constant.other.symbol 273 | # ^^ -constant.other.keyword 274 | "[]": :"[]", 275 | # ^^^^^ constant.other.symbol 276 | # ^^^^^ constant.other.keyword 277 | ] 278 | -------------------------------------------------------------------------------- /tests/syntax_test_attributes.ex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir.sublime-syntax" 2 | 3 | ## Attributes 4 | 5 | @attr :attr 6 | # ^ punctuation.section.arguments.end 7 | # ^ punctuation.section.arguments.begin 8 | #^^^^ entity.name.constant 9 | @attr? :attr? 10 | #^^^^^ entity.name.constant 11 | @attr! :attr! 12 | #^^^^^ entity.name.constant 13 | @attr 14 | #^^^^ variable.other.constant 15 | @attr? 16 | #^^^^^ variable.other.constant 17 | @attr! 18 | #^^^^^ variable.other.constant 19 | 20 | @other @attr 21 | # ^^^^ variable.other.constant 22 | # ^ keyword.operator.attribute 23 | 24 | @fn_attr fn a -> a end 25 | # ^^^ keyword.context.block.end 26 | # ^^ keyword.other.fn 27 | #^^^^^^^ entity.name.constant 28 | @fn_attr.(:a) 29 | # ^ punctuation.section.arguments.end 30 | # ^ punctuation.section.arguments.begin 31 | # ^ punctuation.accessor.dot 32 | #^^^^^^^ variable.other.constant 33 | 34 | @_ :_ 35 | # ^^ constant.other.symbol 36 | #^ entity.name.constant 37 | 38 | @derive Inspect 39 | # ^^^^^^^ constant.other.module 40 | #^^^^^^ support.attr 41 | @deprecated "Use x instead" 42 | # ^^^^^^^^^^^^^^^ string.quoted.double 43 | #^^^^^^^^^^ support.attr 44 | @impl true 45 | # ^^^^ constant.language 46 | #^^^^ support.attr 47 | @file "name.ex" 48 | # ^^^^^^^^^ string.quoted.double 49 | #^^^^ support.attr 50 | @fallback_to_any true 51 | # ^^^^ constant.language 52 | #^^^^^^^^^^^^^^^ support.attr 53 | @behaviour EEx.Engine 54 | # ^^^ constant.other.module 55 | #^^^^^^^^^ support.attr 56 | @vsn "1.0" 57 | # ^^^^^ string.quoted.double 58 | #^^^ support.attr 59 | @compile inline: [compare: 2] 60 | # ^^^^^^^ constant.other.keyword 61 | #^^^^^^^ support.attr 62 | @before_compile Hooks 63 | # ^^^^^ constant.other.module 64 | #^^^^^^^^^^^^^^ support.attr 65 | @after_compile __MODULE__ 66 | # ^^^^^^^^^^ variable.language.special-form 67 | #^^^^^^^^^^^^^ support.attr 68 | @dialyzer {:nowarn_function, func: 1} 69 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.sequence.tuple.elixir 70 | #^^^^^^^^ support.attr 71 | @external_resource file 72 | # ^^^^ variable.other 73 | #^^^^^^^^^^^^^^^^^ support.attr 74 | @on_load :check_on_load 75 | # ^^^^^^^^^^^^^^ constant.other.symbol 76 | #^^^^^^^ support.attr 77 | @on_definition {Hooks, :on_def} 78 | # ^^^^^^^^^^^^^^^^ meta.sequence.tuple.elixir 79 | #^^^^^^^^^^^^^ support.attr 80 | @optional_callbacks f1: 1, 81 | # ^ -punctuation.section.arguments.end 82 | # ^ punctuation.separator.arguments 83 | # ^^^ constant.other.keyword 84 | # ^ punctuation.section.arguments.begin 85 | #^^^^^^^^^^^^^^^^^^ support.attr 86 | f2: 2 87 | # ^ punctuation.section.arguments.end 88 | # ^^^ constant.other.keyword 89 | @enforce_keys [:id, :name] 90 | #^^^^^^^^^^^^ support.attr 91 | 92 | @derive 93 | #^^^^^^ variable.other.constant 94 | @deprecated 95 | #^^^^^^^^^^ variable.other.constant 96 | @impl 97 | #^^^^ variable.other.constant 98 | @file 99 | #^^^^ variable.other.constant 100 | @fallback_to_any 101 | #^^^^^^^^^^^^^^^ variable.other.constant 102 | @behaviour 103 | #^^^^^^^^^ variable.other.constant 104 | @vsn 105 | #^^^ variable.other.constant 106 | @compile 107 | #^^^^^^^ variable.other.constant 108 | @before_compile 109 | #^^^^^^^^^^^^^^ variable.other.constant 110 | @after_compile 111 | #^^^^^^^^^^^^^ variable.other.constant 112 | @dialyzer 113 | #^^^^^^^^ variable.other.constant 114 | @external_resource 115 | #^^^^^^^^^^^^^^^^^ variable.other.constant 116 | @on_load 117 | #^^^^^^^ variable.other.constant 118 | @on_definition 119 | #^^^^^^^^^^^^^ variable.other.constant 120 | @optional_callbacks 121 | #^^^^^^^^^^^^^^^^^^ variable.other.constant 122 | @enforce_keys 123 | #^^^^^^^^^^^^ variable.other.constant 124 | 125 | 126 | @__MODULE__ __MODULE__ 127 | # ^^^^^^^^^^ variable.language 128 | #^^^^^^^^^^ entity.name.constant 129 | @__CALLER__ __CALLER__ 130 | # ^^^^^^^^^^ variable.language 131 | #^^^^^^^^^^ entity.name.constant 132 | @__ENV__ __ENV__ 133 | # ^^^^^^^ variable.language 134 | #^^^^^^^ entity.name.constant 135 | @__DIR__ __DIR__ 136 | # ^^^^^^^ variable.language 137 | #^^^^^^^ entity.name.constant 138 | @__STACKTRACE__ __STACKTRACE__ 139 | # ^^^^^^^^^^^^^^ variable.language 140 | #^^^^^^^^^^^^^^ entity.name.constant 141 | 142 | @__MODULE__ 143 | #^^^^^^^^^^ variable.other.constant 144 | @__CALLER__ 145 | #^^^^^^^^^^ variable.other.constant 146 | @__ENV__ 147 | #^^^^^^^ variable.other.constant 148 | @__DIR__ 149 | #^^^^^^^ variable.other.constant 150 | @__STACKTRACE__ 151 | #^^^^^^^^^^^^^^ variable.other.constant 152 | 153 | 154 | ## Invalid 155 | 156 | @Invalid 157 | #^ invalid.illegal.attribute 158 | -------------------------------------------------------------------------------- /tests/syntax_test_doc.ex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir.sublime-syntax" 2 | 3 | ## Documentation comments 4 | 5 | ## NB: Markdown used to be highlighted inside doc comments, 6 | ## but it had to be removed due to fundamental limitations 7 | ## of the syntax highlighting engine (see issue #31). 8 | 9 | # NB: Check that `def` doesn't match past the newline and consumes the closing `"""`. 10 | @doc """ 11 | Don't insert a comment between `x` and `\"""`. 12 | iex> def x do 13 | ## ^ keyword.other.iex-angle.elixir punctuation.definition.iex.begin.elixir 14 | ## ^^^^^^^^^^^ markup.raw.block.markdown 15 | #^^^ meta.string.elixir 16 | ...(1)> {:ok, _} = Repo.insert(post) 17 | ## ^^^^ constant.other.module.elixir 18 | ## ^^^^^^^^ meta.sequence.tuple.elixir 19 | ## ^ keyword.other.iex-angle.elixir punctuation.definition.iex.begin.elixir 20 | ## ^^^ keyword.other.iex-dots 21 | #^^^ meta.string.elixir 22 | ...(1)> end 23 | ## ^^^ keyword.context.block.end 24 | ## ^ keyword.other.iex-angle.elixir punctuation.definition.iex.begin.elixir 25 | ## ^^^ keyword.other.iex-dots 26 | 27 | iex(1)> [1 + 2, 28 | ## ^ punctuation.section.brackets.begin.elixir 29 | ...(1)> 3] 30 | ## ^ punctuation.section.brackets.end.elixir 31 | ## ^ constant.numeric.integer.elixir 32 | ## ^^^ keyword.other.iex-dots 33 | """m 34 | # ^ variable.other -meta.string.elixir 35 | #^^^ punctuation.definition.string.end 36 | #^^^ meta.string.elixir 37 | 38 | @doc """ 39 | #^^^ support.attr.doc 40 | iex> def x 41 | ## ^^^^^^^^^^^ markup.raw.block.elixir 42 | ##^^^ source.markdown.embedded.elixir 43 | \' \""" #{interpolation} 44 | # ^^^^^^^^^^^^^ variable.other 45 | # ^^^^^^^^^^^^^^^^ meta.interpolation.elixir 46 | # ^^ constant.character.escape.char.elixir 47 | #^^ constant.character.escape.char.elixir 48 | """ 49 | #^^^ punctuation.definition.string.end 50 | 51 | # NB: Check that defmodule doesn't match past the newline and consumes the closing `"""`. 52 | @doc """ 53 | Don't insert a comment between `X` and `\"""`. 54 | defmodule X 55 | # ^^^^^^^^^ -keyword.declaration.module 56 | defmodule X do end 57 | ## ^^^^^^^^^ keyword.declaration.module 58 | ## ^^^^^^^^^^^^ markup.raw.block.markdown 59 | """m 60 | # ^ variable.other -meta.string.elixir 61 | #^^^ punctuation.definition.string.end 62 | #^^^ meta.string.elixir 63 | 64 | @doc """ 65 | ```elixir 66 | ## ^^^^^^ constant.other.language-name.elixir 67 | ##^^^ punctuation.definition.code-block.begin.markdown 68 | @type t :: term 69 | ``` 70 | ##^^^^ punctuation.definition.code-block.end.markdown 71 | Text. 72 | ##^^^^^^ source.markdown.embedded.elixir 73 | ##^^^^^^ meta.string.elixir 74 | """ 75 | 76 | @doc ~S''' 77 | # ^^^^^^ meta.string.elixir 78 | \'''m 79 | '''m 80 | # ^ storage.type.string 81 | #^^^ punctuation.definition.string.end 82 | #^^^^ meta.string.elixir 83 | 84 | @doc ~S""" 85 | # ^^^^^^ meta.string.elixir 86 | \"""m 87 | #^^^ -punctuation.definition.string.end 88 | """m 89 | # ^ storage.type.string 90 | #^^^ punctuation.definition.string.end 91 | #^^^^ meta.string.elixir 92 | 93 | @doc """ 94 | defmodule X do end 95 | ##^^^ source.markdown.embedded.elixir 96 | ## ^^^^^^^^^^^^ markup.raw.block.elixir 97 | """m 98 | # ^ -storage.type.string 99 | #^^^ punctuation.definition.string.end 100 | 101 | @doc ~S""" 102 | \'\S\ \""" #{interpolation} 103 | # ^^^^^^^^^^^^^^^^ -meta.interpolation.elixir 104 | # ^^^ -punctuation.definition.string.end 105 | # ^^ constant.character.escape 106 | #^^^^^^ -constant.character.escape 107 | """m 108 | # ^ storage.type.string 109 | #^^^ punctuation.definition.string.end 110 | 111 | @doc ~S''' 112 | # ^^^ punctuation.definition.string.begin 113 | # ^^ storage.type.string 114 | doc 115 | ##^^^^ source.markdown.embedded.elixir 116 | \'\S\ \""" #{interpolation} 117 | # ^^^^^^^^^^^^^^^^ -meta.interpolation.elixir 118 | # ^^^ -punctuation.definition.string.end 119 | # ^^^^^^ -constant.character.escape 120 | #^^ constant.character.escape 121 | 122 | iex> use Bitwise, only_operators: true 123 | ## ^^^ keyword.other.iex 124 | iex> 1 &&& 1 125 | ## ^^^ keyword.other.iex 126 | '''m 127 | # ^ storage.type.string 128 | #^^^ punctuation.definition.string.end 129 | 130 | # NB: no need to support charlist heredoc. 131 | @doc ''' 132 | doc 133 | #^^^^ -source.markdown.embedded.elixir 134 | '''m 135 | # ^ -storage.type.string 136 | # NB: no need to support `~s"""` which is basically `"""`. 137 | @doc ~s""" 138 | doc 139 | #^^^^ -source.markdown.embedded.elixir 140 | """m 141 | # ^ storage.type.string 142 | 143 | @doc false 144 | # ^ punctuation.section.arguments.end 145 | # ^^^^^ constant.language 146 | # ^ punctuation.section.arguments.begin 147 | #^^^ support.attr.doc 148 | @doc(false) 149 | # ^ punctuation.section.arguments.end 150 | # ^^^^^ constant.language 151 | # ^ punctuation.section.arguments.begin 152 | (@doc false) 153 | # ^ punctuation.section.group.end 154 | #^ punctuation.section.group.begin 155 | 156 | @doc "doc" 157 | ## ^ punctuation.section.arguments.end 158 | ## ^ punctuation.definition.string.end 159 | ## ^^^ source.markdown.embedded.elixir 160 | ## ^ punctuation.definition.string.begin 161 | ## ^ punctuation.section.arguments.begin 162 | @doc("doc") 163 | ## ^ punctuation.section.arguments.end 164 | ## ^ punctuation.definition.string.end 165 | ## ^^^ source.markdown.embedded.elixir 166 | ## ^ punctuation.definition.string.begin 167 | ## ^ punctuation.section.arguments.begin 168 | (@doc "doc") 169 | ## ^^^ source.markdown.embedded.elixir 170 | [@doc "doc"] 171 | ## ^ punctuation.section.brackets.end 172 | ##<- meta.brackets.elixir 173 | 174 | @doc "\n\"escapes\"\n" 175 | ## ^ punctuation.section.arguments.end 176 | ## ^ punctuation.definition.string.end 177 | ## ^^ constant.character.escape.char 178 | ## ^^ constant.character.escape.char 179 | ## ^^^^^^^ source.markdown.embedded.elixir 180 | ## ^^ constant.character.escape.char 181 | ## ^^ constant.character.escape.char 182 | 183 | @doc since: 1.2 184 | # ^^^^^^ constant.other.keyword 185 | @doc guard: true 186 | # ^^^^ constant.language 187 | # ^^^^^^ constant.other.keyword 188 | @doc deprecated: "Use Kernel.length/1 instead" 189 | # ^^^^^^^^^^^ constant.other.keyword 190 | @doc since: "1.2.3", author: "Author" 191 | # ^^^^^^ meta.string.elixir 192 | # ^ punctuation.separator.arguments.elixir 193 | # ^^^^^^^ meta.string.elixir 194 | # ^^^^^^ constant.other.keyword 195 | @doc since: "1.2.3", color: :red, deprecated: "use f/2 instead" 196 | # ^ punctuation.separator.arguments.elixir 197 | # ^^^^ constant.other.symbol 198 | 199 | @doc "Sets foreground color to `#{color}`." 200 | # ^^^^^ variable.other.elixir 201 | # ^^^^^^^^ meta.interpolation.elixir 202 | 203 | quote(do: @doc guard: false) 204 | # ^^^^^ constant.language 205 | # ^^^^^^ constant.other.keyword 206 | # ^^^ support.attr.doc 207 | 208 | @doc # comment 209 | # ^^^^^^^^^ comment.line 210 | #^^^ variable.other.constant -support.attr.doc 211 | @doc == "doc" 212 | # ^^^^^ -source.markdown.embedded.elixir 213 | # ^^ keyword.operator.comparison 214 | # ^ -punctuation.section.group.begin 215 | #^^^ variable.other.constant -support.attr.doc 216 | 217 | @moduledoc "" 218 | #^^^^^^^^^ support.attr.doc 219 | @typedoc "" 220 | #^^^^^^^ support.attr.doc 221 | @shortdoc "" 222 | #^^^^^^^^ support.attr.doc 223 | -------------------------------------------------------------------------------- /tests/syntax_test_numerics.ex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir.sublime-syntax" 2 | 3 | ## Numerics 4 | 5 | ### Integer 6 | 7 | 0 1 9 8 | # ^ constant.numeric.integer 9 | # ^ constant.numeric.integer 10 | #^ constant.numeric.integer 11 | 000 12 | #^^^ constant.numeric 13 | 0_0 14 | #^^^ constant.numeric 15 | 0_1_2_3 16 | #^^^^^^^ constant.numeric 17 | 0123 18 | #^^^^ constant.numeric 19 | 20 | 0__0 0_0__0 21 | # ^^^ invalid.illegal.numeric 22 | # ^^^ invalid.illegal.numeric 23 | 24 | _0 25 | #^ -constant.numeric 26 | _123 27 | #^^^ -constant.numeric 28 | 29 | 30 | ### Float 31 | 32 | 0.0 1.23 33 | # ^^^^ constant.numeric.float 34 | # ^ punctuation.separator.decimal -punctuation.separator.numeric 35 | #^^^ constant.numeric.float 36 | 37 | 0.0 0.0e0 0.0e+0 0.0e-0 38 | # ^^^^^^ constant.numeric.float 39 | # ^^^^^^ constant.numeric.float 40 | # ^^^^^ constant.numeric.float 41 | #^^^ constant.numeric.float 42 | 43 | 0. 44 | # ^ punctuation.accessor.dot -constant.numeric 45 | 0._ 46 | # ^ variable.other.member 47 | 48 | 1..1 49 | #^^ keyword.operator.range 50 | 1... 51 | #^^^ keyword.operator.ellipsis 52 | 53 | 0e0 0E0 0.0e 0.0E 0.0e_ 0.0E_ 0.0E_0 0.0E0_ 0.0E0_0_ 0.0E0__0 54 | # ^^^ invalid.illegal.numeric 55 | # ^ invalid.illegal.numeric 56 | # ^ invalid.illegal.numeric 57 | # ^^^ -constant.numeric 58 | # ^^ -constant.numeric 59 | # ^^ -constant.numeric 60 | # ^ -constant.numeric 61 | # ^ -constant.numeric 62 | # ^^ -constant.numeric 63 | # ^^ -constant.numeric 64 | 65 | 0_0.0 66 | #^^^^^ constant.numeric.float 67 | _123.456 68 | # ^ -punctuation.separator.decimal 69 | #^^^^^^^ -constant.numeric.float 70 | 71 | 72 | ### Binary 73 | 74 | 0b0 0b0_0 0b0_1 75 | # ^^^^^ constant.numeric 76 | # ^^^^^ constant.numeric 77 | # ^ punctuation.separator.numeric 78 | #^^^ constant.numeric.binary 79 | 80 | 0b 0b_ 0b0_ 0b0_0__0 0b0__0 0b2 0b0_2 81 | # ^^ invalid.illegal.numeric 82 | # ^^ -constant.numeric 83 | # ^^^ invalid.illegal.numeric 84 | # ^^^ invalid.illegal.numeric 85 | # ^ invalid.illegal.numeric 86 | # ^^ -constant.numeric 87 | # ^ -constant.numeric 88 | 89 | 0b00..0b01 90 | # ^^^^ constant.numeric 91 | # ^^ keyword.operator.range 92 | #^^^^ constant.numeric 93 | 94 | 95 | ### Hex 96 | 97 | 0x0123456789abcdefABCDEF 98 | # ^ punctuation.separator.numeric 99 | #^^^^^^^^^^^^^^^^^^^^^^^^ constant.numeric.hex 100 | 0x0_1_2_a_b_c_D_E_F 101 | #^^^^^^^^^^^^^^^^^^^ constant.numeric 102 | 103 | 0x 0x_ 0x0_ 0x0__0 0xG 0x0_G 104 | # ^ invalid.illegal.numeric 105 | # ^^ -constant.numeric 106 | # ^^^ invalid.illegal.numeric 107 | # ^ invalid.illegal.numeric 108 | # ^^ -constant.numeric 109 | # ^ -constant.numeric 110 | 111 | 0x00..0x01 112 | # ^^^^ constant.numeric 113 | # ^^ keyword.operator.range 114 | #^^^^ constant.numeric 115 | 116 | ### Octal 117 | 118 | 0o01234567 119 | # ^ punctuation.separator.numeric 120 | #^^^^^^^^^^ constant.numeric.octal 121 | 0o0 0o0_0 0o1 0o1_2_3 122 | # ^^^^^^^ constant.numeric 123 | # ^^^ constant.numeric 124 | # ^^^^^ constant.numeric 125 | #^^^ constant.numeric 126 | 127 | 0o 0o_ 0o0_ 0o8 0o0_8 0o0_7_8 128 | # ^^ invalid.illegal.numeric 129 | # ^^ invalid.illegal.numeric 130 | # ^^ -constant.numeric 131 | # ^ invalid.illegal.numeric 132 | # ^^ -constant.numeric 133 | # ^ -constant.numeric 134 | 135 | 0o00..0o01 136 | # ^^^^ constant.numeric 137 | # ^^ keyword.operator.range 138 | #^^^^ constant.numeric 139 | 140 | 141 | ### Character 142 | 143 | ?? 144 | #^ punctuation.definition.numeric 145 | #^^ constant.numeric.char 146 | 147 | ?: ?. ?x ?\x ?\\ ?\ 148 | # ^^^ constant.numeric.char 149 | # ^^^ constant.numeric.char 150 | # ^^^ constant.numeric.char 151 | # ^^ constant.numeric.char 152 | # ^^ constant.numeric.char 153 | #^^ constant.numeric.char 154 | 155 | ?" ?' ?< ?> 156 | # ^^ constant.numeric.char 157 | # ^^ constant.numeric.char 158 | # ^^ constant.numeric.char 159 | #^^ constant.numeric.char 160 | 161 | ?( ?) ?[ ?] ?{ ?} 162 | # ^^ constant.numeric.char 163 | # ^^ constant.numeric.char 164 | # ^^ constant.numeric.char 165 | # ^^ constant.numeric.char 166 | # ^^ constant.numeric.char 167 | #^^ constant.numeric.char 168 | 169 | ?...?. 170 | # ^^ constant.numeric.char 171 | # ^^ keyword.operator.range 172 | #^^ constant.numeric.char 173 | 174 | ? ? ? 175 | # ^^ invalid.illegal.character-literal 176 | # ^^ constant.numeric.char 177 | # ^ invalid.illegal.character-literal 178 | #^^ constant.numeric.char 179 | -------------------------------------------------------------------------------- /tests/syntax_test_operators.ex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir.sublime-syntax" 2 | 3 | ## Operators: 4 | 5 | [[l | r], l \\ r, l |> r, l -> r, l <- r, l => r, l .. r, l ++ r, l -- r, l <> r] 6 | # ^ variable.other 7 | # ^^ keyword.operator.binary-concat 8 | # ^ variable.other 9 | # ^ variable.other 10 | # ^^ keyword.operator.list-diff 11 | # ^ variable.other 12 | # ^ variable.other 13 | # ^^ keyword.operator.list-concat 14 | # ^ variable.other 15 | # ^ variable.other 16 | # ^^ keyword.operator.range 17 | # ^ variable.other 18 | # ^ variable.other 19 | # ^^ keyword.operator.map-pair 20 | # ^ variable.other 21 | # ^ variable.other 22 | # ^^ keyword.operator.arrow 23 | # ^ variable.other 24 | # ^ variable.other 25 | # ^^ keyword.operator.arrow 26 | # ^ variable.other 27 | # ^ variable.other 28 | # ^^ keyword.operator.pipe 29 | # ^ variable.other 30 | # ^ variable.other 31 | # ^^ keyword.operator.default 32 | # ^ variable.other 33 | # ^ keyword.operator.cons 34 | # ^ variable.other 35 | 36 | [l != r, l == r, l !== r, l === r, l <= r, l < r, l >= r, l > r] 37 | # ^ variable.other 38 | # ^ keyword.operator.comparison 39 | # ^ variable.other 40 | # ^ variable.other 41 | # ^^ keyword.operator.comparison 42 | # ^ variable.other 43 | # ^ variable.other 44 | # ^ keyword.operator.comparison 45 | # ^ variable.other 46 | # ^ variable.other 47 | # ^^ keyword.operator.comparison 48 | # ^ variable.other 49 | # ^ variable.other 50 | # ^^^ keyword.operator.comparison 51 | # ^ variable.other 52 | # ^ variable.other 53 | # ^^^ keyword.operator.comparison 54 | # ^ variable.other 55 | # ^ variable.other 56 | # ^^ keyword.operator.comparison 57 | # ^ variable.other 58 | # ^ variable.other 59 | # ^^ keyword.operator.comparison 60 | #^ variable.other 61 | 62 | [l &&& r, l ||| r, l <<< r, l >>> r, l ~~~ r, l ^^^ r] 63 | # ^ variable.other 64 | # ^^^ keyword.operator.bitwise 65 | # ^ variable.other 66 | # ^ variable.other 67 | # ^^^ keyword.operator.bitwise 68 | # ^ variable.function 69 | # ^ variable.other 70 | # ^^^ keyword.operator.bitwise 71 | # ^ variable.other 72 | # ^ variable.other 73 | # ^^^ keyword.operator.bitwise 74 | # ^ variable.other 75 | # ^ variable.other 76 | # ^^^ keyword.operator.bitwise 77 | # ^ variable.other 78 | # ^ variable.other 79 | # ^^^ keyword.operator.bitwise 80 | #^ variable.other 81 | 82 | [l && r, l and r, l || r, l or r, l xor r] 83 | # ^ variable.other 84 | # ^ variable.other 85 | # ^^ keyword.operator.logical 86 | # ^ variable.other 87 | # ^ variable.other 88 | # ^^ keyword.operator.logical 89 | # ^ variable.other 90 | # ^ variable.other 91 | # ^^^ keyword.operator.logical 92 | # ^ variable.other 93 | # ^ variable.other 94 | # ^^ keyword.operator.logical 95 | #^ variable.other 96 | 97 | 98 | [^u, &u/1, @u, l ... r, l =~ r, l <~> r, l <~ r, l <<~ r, l ~> r, l ~>> r, l <|> r] 99 | # ^ variable.other 100 | # ^^^ keyword.operator.pipe 101 | # ^ variable.other 102 | # ^ variable.other 103 | # ^^^ keyword.operator.pipe 104 | # ^ variable.other 105 | # ^ variable.other 106 | # ^^ keyword.operator.pipe 107 | # ^ variable.other 108 | # ^ variable.other 109 | # ^^^ keyword.operator.pipe 110 | # ^ variable.other 111 | # ^ variable.other 112 | # ^^ keyword.operator.pipe 113 | # ^ variable.other 114 | # ^ variable.other 115 | # ^^^ keyword.operator.pipe 116 | # ^ variable.other 117 | # ^ variable.other 118 | # ^^ keyword.operator.regex 119 | # ^ variable.other 120 | # ^ variable.other 121 | # ^^^ keyword.operator.ellipsis 122 | # ^ variable.function 123 | # ^ variable.other 124 | # ^ keyword.operator.attribute 125 | # ^ constant.numeric.arity 126 | # ^ punctuation.accessor.arity 127 | # ^ variable.other.capture 128 | # ^ keyword.operator.capture 129 | # ^ variable.other 130 | #^ keyword.operator.pin 131 | 132 | [not u, !u, -u, +u, l not in r, l :: r] 133 | # ^ variable.other 134 | # ^^ keyword.operator.colon 135 | # ^ variable.other 136 | # ^ variable.other 137 | # ^^ keyword.operator.logical 138 | # ^^^ keyword.operator.logical 139 | # ^ variable.other 140 | # ^ variable.other 141 | # ^ keyword.operator.arithmetic 142 | # ^ variable.other 143 | # ^ keyword.operator.arithmetic 144 | # ^ variable.other 145 | # ^ keyword.operator.logical 146 | # ^ variable.other 147 | #^^^ keyword.operator.logical 148 | 149 | [l - r, l + r, l * r, l / r, l // r] 150 | # ^ variable.other 151 | # ^^ keyword.operator.arithmetic 152 | # ^ variable.other 153 | # ^ keyword.operator.arithmetic 154 | # ^ variable.other 155 | # ^ variable.other 156 | # ^ keyword.operator.arithmetic 157 | # ^ variable.other 158 | # ^ variable.other 159 | # ^ keyword.operator.arithmetic 160 | # ^ variable.other 161 | # ^ variable.other 162 | # ^ keyword.operator.arithmetic 163 | #^ variable.other 164 | 165 | [(l = r), (l; r), l.r] 166 | # ^ variable.other 167 | # ^ punctuation.accessor.dot 168 | # ^ variable.other 169 | # ^ variable.other 170 | # ^ keyword.operator.semicolon 171 | # ^ variable.other 172 | # ^ variable.other 173 | # ^ keyword.operator.match 174 | # ^ variable.other 175 | 176 | l | r 177 | # ^ keyword.operator.union 178 | 179 | l ::::: r 180 | # ^ variable.other 181 | # ^^ keyword.operator.colon 182 | # ^^^ constant.other.symbol 183 | #<- variable.function 184 | -------------------------------------------------------------------------------- /tests/syntax_test_regexp.ex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir.sublime-syntax" 2 | 3 | # Quoted sequence: 4 | ~r"\Qnot terminating \Q is not an error" 5 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.literal.pcree 6 | # ^^ keyword.control.quote.pcree 7 | 8 | ~r"\Q?+*{1} ^ $ . | ) ( $ \ \s \b \N [] \g<1> 9 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.literal.pcree 10 | # ^^ keyword.control.quote.pcree punctuation.definition.quote.begin.pcree 11 | \E" 12 | # <- keyword.control.quote.pcree punctuation.definition.quote.end.pcree 13 | 14 | # Quantifier after quoted sequence: 15 | ~r"\Qxyz\E{0,2}" 16 | # ^^^^^ keyword.operator.quantifier.pcree 17 | 18 | 19 | ~r"{1}"~r"+"~r"\K*"~r"\b+"~r"|?" 20 | # ^ invalid.illegal.unexpected-quantifier.pcree 21 | # ^ invalid.illegal.unexpected-quantifier.pcree 22 | # ^ invalid.illegal.unexpected-quantifier.pcree 23 | # ^ invalid.illegal.unexpected-quantifier.pcree 24 | # ^^^ invalid.illegal.unexpected-quantifier.pcree 25 | 26 | # Subroutine calls: 27 | ~r"\g\g'n'\g<-1>\g<1>\g'-1'\g'1'\g'\g''\g<\g<>\g'aä'\g\g\g<-0>" 28 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ keyword.other.subroutine.pcree 29 | ~r"(?-1)(?0)(?+1)(?R)(?R(?P>n)(?&n)(?P>n(?&n(?P>aä)(?&aä)(?P>aä(?&aä(?P>(?P>)(?&(?&)" 30 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ keyword.other.subroutine.pcree 31 | 32 | # Back references: 33 | ~r"\g{2}\g-2\g0\g\g{as\g{\g{}\g{aä}\g0\g(?P=n)(?P=n(?P=ä(?P=ä)(?P=(?P=)" 34 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ keyword.other.back-reference.pcree 35 | ~r"\k{n}\k'n'\k\k\k{}\k<>\k''\k{\k<\k'\k{a\k\k'aä'" 36 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ keyword.other.back-reference.pcree 37 | # ^ invalid.illegal.back-reference.pcree 38 | # ^ variable.other.back-reference.pcree 39 | # ^ invalid.illegal.back-reference.pcree 40 | # ^ variable.other.back-reference.pcree 41 | # ^ invalid.illegal.back-reference.pcree 42 | # ^^ invalid.illegal.back-reference.pcree 43 | # ^^ invalid.illegal.back-reference.pcree 44 | # ^^ invalid.illegal.back-reference.pcree 45 | # ^ invalid.illegal.back-reference.pcree 46 | # ^ invalid.illegal.back-reference.pcree 47 | # ^ invalid.illegal.back-reference.pcree 48 | # ^^ invalid.illegal.back-reference.pcree 49 | # ^^ invalid.illegal.back-reference.pcree 50 | # ^^ invalid.illegal.back-reference.pcree 51 | # ^^ invalid.illegal.back-reference.pcree 52 | # ^ variable.other.back-reference.pcree 53 | # ^ variable.other.back-reference.pcree 54 | # ^ variable.other.back-reference.pcree 55 | 56 | # Escape sequences: 57 | ~r"\d\h\s\v\w\D\H\S\V\W" 58 | # ^^^^^^^^^^^^^^^^^^^^ constant.other.escape-sequence.pcree 59 | ~r"\p{C}\p{Cc}\p{Cf}\p{Cn}\p{Co}\p{Cs}\p{L}\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{M}\p{Mc}\p{Me}\p{Mn}\p{N}\p{Nd}\p{Nl}\p{No}\p{P}\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{S}\p{Sc}\p{Sk}\p{Sm}\p{So}\p{Z}\p{Zl}\p{Zp}\p{Zs}" 60 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant.other.escape-sequence.general-category.pcree 61 | ~r"\p{C}\p{L}\p{M}\p{N}\p{P}\p{S}\p{Z}\pC\pL\pM\pN\pP\pS\pZ" 62 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant.other.escape-sequence.general-category.pcree 63 | ~r"\p{123}\p\p{}" 64 | # ^^ invalid.illegal.general-category.pcree 65 | # ^^ invalid.illegal.general-category.pcree 66 | # ^^^ invalid.illegal.general-category.pcree 67 | # ^^^^^^^^^^^^^ constant.other.escape-sequence.general-category.pcree 68 | ~r"\K\R\X\?\*\+\.\x00\00\07\o\o{}\o{84}\o{0}\cX\cä\a\e\f\n\r\t" 69 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant.character.escape 70 | 71 | # Assertions: 72 | ~r"^\b\B\A\z\Z\G$" 73 | # ^^^^^^^^^^^^^^ keyword.control.anchor 74 | 75 | # Class sets: 76 | ~r"[.+*?{1}()[|^$][\N\b\x\xf\xff\A\B\g\G\k\K\R\X\z\Z]" 77 | # ^^^^^^^^^^^^^^^^^^^^ constant.character.escape.pcree 78 | # ^^^^^^^^^ constant.character.escape.hex.pcree 79 | # ^^ constant.character.escape.backspace.pcree 80 | # ^^ invalid.illegal.escape-sequence.pcree 81 | # ^^^^^^^^^^^^^ meta.literal.pcree 82 | # ^^^^^^^^^^^^^^^ meta.set.pcree 83 | ~r"[[:>:]]" 84 | # ^^^^^ invalid.deprecated.word-boundary.pcree 85 | ~r"[a]][]][^]][^a]" 86 | # ^ meta.literal.pcree 87 | # ^ punctuation.definition.set.end.pcree 88 | # ^ meta.literal.pcree 89 | # ^ keyword.operator.negation.pcree 90 | # ^ punctuation.definition.set.end.pcree 91 | # ^ meta.literal.pcree 92 | # ^ meta.literal.pcree 93 | # ^ meta.literal.pcree 94 | ~r"[ 95 | # ^ meta.set.pcree meta.literal.pcree 96 | ]" 97 | 98 | ~r"[a-b-c] [-a-z-] [\d-\s \w-\\] [\x0-\x1] [\x00-\xff] [\000-\007] [\0-\18]" 99 | # ^ meta.literal.pcree 100 | # ^^^^ constant.other.range.pcree 101 | # ^ keyword.operator.range.pcree 102 | # ^^^^ constant.other.range.pcree 103 | # ^^^^ constant.other.range.pcree 104 | # ^^^^ constant.other.range.pcree 105 | # ^^^ constant.other.range.pcree 106 | # ^^^ constant.other.range.pcree 107 | # ^^ constant.other.range.pcree 108 | # ^^ invalid.illegal.range.pcree 109 | # ^^ invalid.illegal.range.pcree 110 | # ^^ invalid.illegal.range.pcree 111 | # ^ meta.literal.pcree 112 | # ^ keyword.operator.range.pcree 113 | # ^ meta.literal.pcree 114 | # ^^ meta.literal.pcree 115 | ~r"[-] [--] [---] [\--\-] [[--] [--[] [[-[] [[-] [[-\]] [\[-\]]" 116 | # ^^ constant.other.range.pcree 117 | # ^^ constant.other.range.pcree 118 | # ^^ meta.literal.pcree 119 | # ^ constant.other.range.pcree 120 | # ^ constant.other.range.pcree 121 | # ^ constant.other.range.pcree 122 | # ^ constant.other.range.pcree 123 | # ^ constant.other.range.pcree 124 | # ^^ constant.other.range.pcree 125 | # ^^ constant.other.range.pcree 126 | # ^ keyword.operator.range.pcree 127 | # ^ punctuation.definition.set.end.pcree 128 | # ^^ meta.literal.pcree 129 | # ^ meta.literal.pcree 130 | 131 | # Inline options: 132 | ~r"(*NO_START_OPT)(*UTF)(*UTF8)(*UCP)(*CRLF)(*CR)(*LF)(*ANYCRLF)(*ANY)(*BSR_ANYCRLF)(*BSR_UNICODE)(*LIMIT_MATCH=)(*LIMIT_RECURSION=)(*ANY)" 133 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ keyword.control.flag.pcree 134 | ~r"(*LIMIT_MATCH=1)(*LIMIT_RECURSION=2)" 135 | # ^ constant.numeric.integer.decimal.pcree 136 | # ^ constant.numeric.integer.decimal.pcree 137 | 138 | # Backtracking verbs: 139 | ~r"(*ACCEPT)(*COMMIT)(*FAIL)(*F)(*MARK)(*THEN)(*PRUNE)(*SKIP)" 140 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.backtracking.pcree 141 | ~r"(*ACCEPT:)(*COMMIT:)(*FAIL:)(*F:)(*MARK)(*THEN:)(*PRUNE:)(*SKIP:)(*:name)" 142 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.backtracking.pcree 143 | ~r"(*ACCEPT:n)(*COMMIT:n)(*FAIL:n)(*F:n)(*MARK:)(*:)" 144 | # ^ invalid.illegal.backtracking-verb.pcree 145 | # ^ invalid.illegal.backtracking-verb.pcree 146 | # ^ invalid.illegal.backtracking-verb.pcree 147 | # ^ invalid.illegal.backtracking-verb.pcree 148 | # ^ invalid.illegal.backtracking-verb.pcree 149 | # ^ invalid.illegal.backtracking-verb.pcree 150 | 151 | # Groups: 152 | ~r"()" 153 | # ^^ meta.group.pcree keyword.control.group.pcree 154 | # ^ punctuation.definition.group.end.pcree 155 | # ^ punctuation.definition.group.begin.pcree 156 | ~r")" 157 | # ^ invalid.illegal.unmatched-brace.pcree 158 | 159 | # Internal options: 160 | ~r"(?i)(?m)(?s)(?x)(?J)(?U)(?X)(?-)(?-:)(?-i:)" 161 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.group.pcree 162 | ~r"(?i:)(?m:)(?s:)(?x:)(?J:)(?U:)(?X:)" 163 | 164 | ~r"(?=)(?!)(?<=)(?)(?:)(?|)" 165 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.group.pcree 166 | 167 | ~r"(?P)(?)(?'name')" 168 | # ^^^^ entity.name.capture-group.pcree 169 | # ^^^^ entity.name.capture-group.pcree 170 | # ^^^^ entity.name.capture-group.pcree 171 | ~r"(?<ä>)(?P<ä>)(?'ä')(?P<>)(?<>)(?P'name')(?<)(?')" 172 | # ^ invalid.illegal.unexpected-quantifier.pcree 173 | # ^ invalid.illegal.unexpected-quantifier.pcree 174 | # ^ invalid.illegal.named-capture.pcree 175 | # ^^ invalid.illegal.named-capture.pcree 176 | # ^^ invalid.illegal.named-capture.pcree 177 | # ^ invalid.illegal.named-capture.pcree 178 | # ^ invalid.illegal.named-capture.pcree 179 | # ^ invalid.illegal.named-capture.pcree 180 | 181 | ~r"(?>atomic group)(?:non-capturing group)(?|reset group)" 182 | # ^^ keyword.control.reset-numbers-group.pcree 183 | # ^^ keyword.control.non-capturing-group.pcree 184 | # ^^ keyword.control.atomic-group.pcree 185 | 186 | ~r"(?(Rx)t|f) (?(R)t|f) (?(R3)t|f) (?(R&r)t|f) (?(DEFINE)t|f) (?(DEFINEx)t|f)" 187 | # ^^^^^^^ variable.other.back-reference.pcree 188 | # ^^^^^^ keyword.other.conditional.definition.pcree 189 | # ^ variable.other.recursion.pcree 190 | # ^ variable.other.recursion.pcree 191 | # ^ keyword.operator.recursion.pcree 192 | # ^^ variable.other.back-reference.pcree 193 | ~r"(?(condition)t|f) (?('condition')t|f) (?()t|f)" 194 | # ^^^^^^^^^ variable.other.back-reference.pcree 195 | # ^^^^^^^^^ variable.other.back-reference.pcree 196 | # ^^^^^^^^^ variable.other.back-reference.pcree 197 | ~r"(?(?=)t|f) (?(?!)t|f) (?(?<=)t|f) (?(?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz{}~] 286 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.literal.pcree 287 | ~r"äöüßȩşḑḩļçáóéíú€¢" 288 | # ^^^^^^^^^^^^^^^^^ meta.literal.pcree 289 | ~r" 290 | # ^ meta.literal.pcree 291 | " 292 | 293 | # Elixir string interpolations: 294 | 295 | ~r"_{0,#{1}} # #{2}" 296 | # ^^^^ meta.interpolation.elixir 297 | # ^^ comment.line.number-sign.pcree 298 | # ^^^^ meta.interpolation.elixir 299 | -------------------------------------------------------------------------------- /tests/syntax_test_sql.ex.sql: -------------------------------------------------------------------------------- 1 | -- SYNTAX TEST "SQL (Elixir).sublime-syntax" 2 | 3 | -- Identifiers 4 | 5 | SELECT * FROM posts; 6 | -- ^ constant.other.wildcard.asterisk 7 | 8 | SELECT posts."column"; 9 | -- ^^^^^^ string 10 | -- ^^^^^ constant.other.table-name 11 | 12 | SELECT posts.*; 13 | -- ^ constant.other.wildcard.asterisk 14 | -- ^ punctuation.accessor.dot 15 | -- ^^^^^ constant.other.table-name 16 | SELECT json_object(posts.*); 17 | -- ^ constant.other.wildcard.asterisk 18 | -- ^ punctuation.accessor.dot 19 | -- ^^^^^ constant.other.table-name 20 | 21 | SELECT array_agg(vote) FILTER (WHERE vote IS NOT NULL) FROM posts 22 | -- ^^^^^ variable.other 23 | -- ^^^^ keyword.other.DML 24 | -- ^^^^ constant.language.null 25 | -- ^^^ keyword.other 26 | -- ^^ keyword.other 27 | -- ^^^^ variable.other 28 | -- ^^^^^ keyword.other.DML 29 | -- ^^^^^^ keyword.other 30 | -- ^^^^ variable.other 31 | -- ^^^^^^^^^ variable.function 32 | 33 | -- Numbers 34 | 35 | SELECT 123, 123., 123.45, .123; 36 | -- ^ punctuation.separator.decimal 37 | -- ^^^^ constant.numeric 38 | -- ^ punctuation.separator.decimal 39 | -- ^^^^^^ constant.numeric 40 | -- ^^^^ constant.numeric 41 | -- ^^^ constant.numeric 42 | SELECT 123e01, 123.e01, 123.45e01, .123e01; 43 | -- ^ punctuation.separator.decimal 44 | -- ^^^^^^^ constant.numeric 45 | -- ^ punctuation.separator.decimal 46 | -- ^^^^^^^^^ constant.numeric 47 | -- ^ punctuation.separator.decimal 48 | -- ^^^^^^^ constant.numeric 49 | -- ^^^^^^ constant.numeric 50 | SELECT 123e-01, 123.e-01, 123.45e-01, .123e-01; 51 | -- ^ punctuation.separator.decimal 52 | -- ^^^^^^^ constant.numeric 53 | -- ^ punctuation.separator.decimal 54 | -- ^^^^^^^^^^ constant.numeric 55 | -- ^ punctuation.separator.decimal 56 | -- ^^^^^^^^ constant.numeric 57 | -- ^^^^^^^ constant.numeric 58 | SELECT 123e+01, 123.e+01, 123.45e+01, .123e+01; 59 | -- ^ punctuation.separator.decimal 60 | -- ^^^^^^^ constant.numeric 61 | -- ^ punctuation.separator.decimal 62 | -- ^^^^^^^^^^ constant.numeric 63 | -- ^ punctuation.separator.decimal 64 | -- ^^^^^^^^ constant.numeric 65 | -- ^^^^^^^ constant.numeric 66 | SELECT 1E0, 1.E1, 1.0E1, .1E1; 67 | -- ^^^^ constant.numeric 68 | -- ^^^^^ constant.numeric 69 | -- ^^^^ constant.numeric 70 | -- ^^^ constant.numeric 71 | SELECT 1 + .e01; 72 | -- ^ - punctuation.separator.decimal 73 | -- ^^^^ - constant.numeric 74 | 75 | -------------------------------------------------------------------------------- /tests/syntax_test_surface.ex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir.sublime-syntax" 2 | 3 | ## Surface templates 4 | 5 | ~F""" 6 | #^^ meta.string.elixir storage.type.string.elixir 7 | # ^^^ meta.string.elixir punctuation.definition.string.begin.elixir 8 | 9 | {!-- Comment --} 10 | # ^^^ punctuation.definition.comment.end.surface 11 | # ^^^^ punctuation.definition.comment.begin.surface 12 | # ^^^^^^^^^^^^^^^^ meta.embedded.surface comment.block.surface 13 | 14 | 15 | # ^ punctuation.section.embedded.end.elixir - source.elixir.embedded 16 | # ^ source.elixir.embedded.html 17 | # ^ punctuation.section.embedded.begin.elixir - source.elixir.embedded 18 | # ^ entity.other.attribute-name.html 19 | # ^^^^^^^^^ entity.name.tag.begin.surface 20 | # ^^^ entity.name.tag.begin.surface 21 | # ^^^^ entity.name.tag.begin.surface 22 | 23 | # ^^^^^^^^^ entity.name.tag.end.surface 24 | # ^^^ entity.name.tag.end.surface 25 | # ^^^^ entity.name.tag.end.surface 26 | """ 27 | #^^ meta.string.elixir punctuation.definition.string.end.elixir 28 | #<- meta.string.elixir punctuation.definition.string.end.elixir 29 | -------------------------------------------------------------------------------- /tests/syntax_test_template.eex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "EEx.sublime-syntax" 2 | 3 | ## EEx inside plain text 4 | 5 | <%= if @html do %> 6 | # ^^ punctuation.section.embedded.end.eex 7 | # ^ meta.block.elixir 8 | # ^^ meta.block.elixir punctuation.section.block.begin.elixir keyword.context.block.do.elixir 9 | # ^^^^ variable.other.constant.elixir 10 | # ^ keyword.operator.attribute.elixir 11 | # ^ punctuation.section.arguments.begin.elixir 12 | # ^^^^^^^^^ meta.function-call.arguments.elixir 13 | # ^^ keyword.control.conditional.elixir 14 | #^^^ punctuation.section.embedded.begin.eex 15 | #^^^^^^^^^^^^^^^^^^ meta.embedded.eex 16 |
    Some text
    17 | #^^^^^^^^^^^^^^^^^^^^^^ text.eex 18 | <%= else %> 19 | # ^^ punctuation.section.embedded.end.eex 20 | # ^^^^ keyword.control.conditional.else.elixir 21 | #^^^ punctuation.section.embedded.begin.eex 22 | #^^^^^^^^^^^ meta.embedded.eex 23 | Some text 24 | # ^^^^^^^^^^ text.eex 25 | <% end %> 26 | # ^^ punctuation.section.embedded.end.eex 27 | # ^^^ keyword.context.block.end.elixir 28 | #^^ punctuation.section.embedded.begin.eex 29 | #^^^^^^^^^ meta.embedded.eex 30 | 31 | 32 | ## Comments 33 | 34 | <% # Comment %> 35 | # ^^ punctuation.section.embedded.end.eex 36 | # ^ punctuation.definition.comment.elixir 37 | # ^^^^^^^^^^ comment.line.number-sign.elixir 38 | #^^ punctuation.section.embedded.begin.eex 39 | #^^^^^^^^^^^^^^^ meta.embedded.eex 40 | <% # Comment 41 | # ^ punctuation.definition.comment.elixir 42 | # ^^^^^^^^^^ comment.line.number-sign.elixir 43 | #^^ punctuation.section.embedded.begin.eex 44 | #^^^^^^^^^^^^^ meta.embedded.eex 45 | x = 1 46 | # ^ constant.numeric.integer.elixir 47 | # ^ keyword.operator.match.elixir 48 | # ^ variable.other.elixir 49 | #^^^^^^^^^ meta.embedded.eex 50 | # Comment %> 51 | # ^^ punctuation.section.embedded.end.eex 52 | # ^ punctuation.definition.comment.elixir 53 | # ^^^^^^^^^^ comment.line.number-sign.elixir 54 | #^^^^^^^^^^^^^^^ meta.embedded.eex 55 | 56 | 57 | <%# Comment %> 58 | # ^^ punctuation.section.embedded.end.eex 59 | # ^ punctuation.definition.comment.begin.eex 60 | #^^ punctuation.section.embedded.begin.eex 61 | #^^^^^^^^^^^^^^ meta.embedded.eex comment.block.eex 62 | 63 | <%# Comment 64 | # ^ punctuation.definition.comment.begin.eex 65 | #^^ punctuation.section.embedded.begin.eex 66 | #^^^^^^^^^^^^ meta.embedded.eex comment.block.eex - text.html 67 | %> 68 | #^^ punctuation.section.embedded.end.eex 69 | #^^ meta.embedded.eex comment.block.eex 70 | 71 | <%!-- Comment 72 | # ^^^ punctuation.definition.comment.begin.eex 73 | #^^ punctuation.section.embedded.begin.eex 74 | #^^^^^^^^^^^^^^ meta.embedded.eex comment.block.eex - text.html 75 | --%> 76 | # ^^ punctuation.section.embedded.end.eex 77 | #^^ punctuation.definition.comment.end.eex 78 | #^^^^ meta.embedded.eex comment.block.eex 79 | -------------------------------------------------------------------------------- /tests/syntax_test_template.ex.eex: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "Elixir (EEx).sublime-syntax" 2 | 3 | # EEx inside Elixir code 4 | 5 | [ 6 | <%= if @ecto do %> 7 | # ^ keyword.operator.attribute 8 | :ecto, 9 | <% end %><%= if @html do %> 10 | # ^^^ keyword.context.block.end 11 | # ^^ keyword.control.conditional 12 | :html 13 | <% end %> 14 | # ^^^ keyword.context.block.end 15 | ] 16 | #<- -invalid 17 | 18 | defmodule <%= @module %>.View do 19 | # ^^^^ entity.name.namespace 20 | # ^ punctuation.accessor.dot 21 | # ^^ punctuation.section.embedded.end.eex 22 | # ^ keyword.operator.attribute 23 | # ^^^ punctuation.section.embedded.begin.eex 24 | end 25 | 26 | alias <%= @web_namespace %>.Router.Helpers, as: Routes 27 | # ^^^^^^ entity.name.namespace 28 | # ^ punctuation.accessor.dot 29 | # ^^^ punctuation.section.embedded.begin.eex 30 | 31 | :<%= @key %> 32 | #<- constant.other.symbol punctuation.definition.constant.begin 33 | <%= @key %>: 34 | # ^ constant.other.keyword punctuation.definition.constant.end 35 | <%= @key %>_atom: 36 | # ^ punctuation.definition.constant.end 37 | # ^^^^^^ constant.other.keyword 38 | 39 | 40 | # NB: due to the workaround in the syntax file EEx tags 41 | # are also highlighted inside strings. 42 | # FIXME: make negative check with "-entity" when solved. 43 | "<%= string %>" 44 | # ^^ punctuation.section.embedded.end.eex 45 | #^^^ punctuation.section.embedded.begin.eex 46 | 47 | M1.<%= M2 %>.f() 48 | # ^ punctuation.section.arguments.end 49 | # ^ punctuation.section.arguments.begin 50 | # ^ variable.function 51 | # ^ punctuation.accessor.dot 52 | # ^^ punctuation.section.embedded.end.eex 53 | # ^^ constant.other.module 54 | # ^^^ punctuation.section.embedded.begin.eex 55 | # ^ punctuation.accessor.dot 56 | 57 | x.<%= :member %>() 58 | # ^ punctuation.section.arguments.end 59 | # ^ punctuation.section.arguments.begin 60 | # ^^ punctuation.section.embedded.end.eex 61 | # ^^^^^^^ constant.other.symbol 62 | # ^^^ punctuation.section.embedded.begin.eex 63 | #^ punctuation.accessor.dot 64 | 65 | @type <%= :t %> :: any 66 | # ^^ punctuation.section.embedded.end.eex 67 | # ^^^ punctuation.section.embedded.begin.eex 68 | ^^^ support.type 69 | 70 | @spec <%= :name %>(any) 71 | # ^^^ support.type 72 | # ^^ punctuation.section.embedded.end.eex 73 | # ^^^ punctuation.section.embedded.begin.eex 74 | ^ punctuation.definition.parameters.end 75 | ^ punctuation.definition.parameters.begin 76 | 77 | &<%= %> 78 | # ^^ punctuation.section.embedded.end.eex 79 | # ^^^ punctuation.section.embedded.begin.eex 80 | #^ keyword.operator.capture 81 | 82 | # <%= @web_app_name %> 83 | # ^^ punctuation.section.embedded.end.eex 84 | # ^^^ punctuation.section.embedded.begin.eex 85 | #^^^^^^^^^^^^^^^^^^^^^^ comment.line.number-sign.elixir 86 | #<- comment.line.number-sign.elixir punctuation.definition.comment.elixir 87 | 88 | 89 | <%# Comment 90 | # ^ punctuation.definition.comment.begin.eex 91 | #^^ punctuation.section.embedded.begin.eex 92 | #^^^^^^^^^^^^ meta.embedded.eex comment.block.eex - text.html 93 | %> 94 | #^^ punctuation.section.embedded.end.eex 95 | #^^ meta.embedded.eex comment.block.eex - text.html 96 | 97 | <%!-- Comment 98 | # ^^^ punctuation.definition.comment.begin.eex 99 | #^^ punctuation.section.embedded.begin.eex 100 | #^^^^^^^^^^^^^^ meta.embedded.eex comment.block.eex - text.html 101 | --%> 102 | # ^^ punctuation.section.embedded.end.eex 103 | #^^ punctuation.definition.comment.end.eex 104 | #^^^^ meta.embedded.eex comment.block.eex - text.html 105 | -------------------------------------------------------------------------------- /tests/syntax_test_template.html.eex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | "> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% # Comment %> 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | <%# Comment 26 | 27 | 28 | 29 | Block 30 | 31 | %> 32 | 33 | 34 | 35 |
    36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | <<%= @tag %> attr="" attr=<%= @value %> <%= @attr %>="" <%= @attr %>=<%= @value %>> 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | > 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | < <%= @tag %> <%= @attr %>> 80 | 81 | 82 | > 83 | 84 | 85 | -name/> 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | =""/> 94 | 95 | 96 | 97 | 98 | /> 99 | 100 | 101 | 102 | 103 | 104 | <% func arg %> 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | <%= if true? do %> 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | <% end %> 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | <%% quoted :code %> 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | <%%= quoted :result %> 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /tests/syntax_test_template.html.heex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | <%!-- Multi-line 22 | 23 | 24 | 25 | 26 | comment --%> 27 | 28 | 29 | 30 | 31 | 32 | <% # Comment %> 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | <%# Comment %> 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | {"1" <> "2"} 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | <:col :let={user}><%= user.id %>
    63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
    79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
    87 | 88 | 89 |

    90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | <.table rows={@users}> 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | <:col let={user} label="Name"> 119 | 120 | 121 | 122 | 123 | 124 | 125 | <%= user.name %> 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | <{=@a}a /> 239 | 240 | 241 | <% func arg %> 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | <%= if true? do %> 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | <% end %> 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | <%% quoted :code %> 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | <%%= quoted :result %> 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | -------------------------------------------------------------------------------- /tests/syntax_test_template.sface: -------------------------------------------------------------------------------- 1 | # SYNTAX TEST "HTML (Surface).sublime-syntax" 2 | 3 | 4 | # ^ invalid 5 | # ^ invalid 6 | # ^^^^^^^^ - meta.embedded 7 | # ^^^^^^ meta.embedded.surface 8 | <%# No EEx comment %> 9 | # ^^^^^^^^^^^^^^^^^^^^^ meta.tag.other.surface - meta.embedded - comment 10 | 11 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.embedded - comment 12 | # ^^^^^^^^^^^^^^^^^^^^ meta.string.html - meta.embedded - comment 13 | 14 | 15 | 16 | # ^^^^^^^^^^ string.quoted.double 17 | # ^ punctuation.separator.key-value 18 | # ^^ entity 19 | # ^ meta.embedded.surface punctuation.section.embedded.end.elixir - source 20 | # ^^^^ constant.other.keyword 21 | # ^^^^^^ constant.other.keyword 22 | # ^^^^^^^^^^^^^^^^^^^^^^ meta.embedded.surface source.elixir.embedded.html 23 | # ^ meta.embedded.surface punctuation.section.embedded.begin.elixir - source 24 | # ^ punctuation.definition.attribute.begin.surface 25 | #^^^^^^ entity.name.tag.begin.surface 26 | {#for i <- 1..max} 27 | # ^^^^^^^^^^^^ source.elixir.embedded.html meta.function-call.arguments.elixir 28 | # ^^^ keyword.control.loop.surface 29 | # ^^^^^^^^^^^^^^^^^^ meta.embedded.surface 30 | 31 | # ^^^^^^^^ meta.attribute-with-value.style.html meta.embedded.surface 32 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.attribute-with-value.class.html meta.embedded.surface 33 | # ^^^^^ meta.attribute-with-value.id.html meta.embedded.surface 34 | # ^^ meta.tag.inline.any.html meta.attribute-with-value entity.other.attribute-name 35 | 36 | # ^ meta.attribute-with-value.class.html meta.string.html - meta.embedded 37 | # ^ - punctuation.section.embedded.end.elixir - source 38 | # ^^^^^^^^^ - variable.other.constant.elixir 39 | # ^ - keyword.operator.attribute.elixir 40 | # ^^^^^^^^^^ - source 41 | # ^ - punctuation.section.embedded.begin.elixir - source 42 | # ^^^^^^^^^^^^^^^ meta.attribute-with-value.class.html meta.class-name.html meta.string.html - meta.embedded.surface 43 | # ^^^^^^^^ meta.attribute-with-value.class.html meta.string.html - meta.embedded 44 | 45 | {i + 1} 46 | # ^ punctuation.section.embedded.end.elixir - source 47 | # ^^^^^ source.elixir.embedded.html 48 | # ^ punctuation.section.embedded.begin.elixir - source 49 | # ^^^^^^^ meta.embedded.surface 50 | {#else} 51 | # ^^^^ keyword.control.conditional.surface 52 | {#elsei} 53 | # ^^^^^ keyword.control.surface 54 | {#elseif x == 0} 55 | # ^ punctuation.section.embedded.end.surface 56 | # ^^^^^^ source.elixir.embedded.html 57 | # ^^^^^^ keyword.control.conditional.surface 58 | # ^^ punctuation.section.embedded.begin.surface 59 | # ^^^^^^^^^^^^^^^^ meta.embedded.surface 60 | {/for} 61 | # ^ punctuation.section.embedded.end.surface 62 | # ^^^ keyword.control.loop.surface 63 | # ^^ punctuation.section.embedded.begin.surface 64 | # ^^^^^^ meta.embedded.surface 65 | 66 | # ^^^^^^ entity.name.tag.end.surface 67 | 68 | 69 | # ^^^^^^^^^ variable.function.surface - entity.name 70 | #^^^^^^^^^^^^^^^^^^^^^^^ meta.tag.other.surface 71 | 72 | # ^^^^^^^^^ entity.name.tag.begin.surface 73 | # ^ punctuation.accessor.dot.surface 74 | # ^^^ entity.name.tag.begin.surface 75 | # ^ punctuation.accessor.dot.surface 76 | #^^^^ entity.name.tag.begin.surface 77 | #^^^^^^^^^^^^^^^^^^^^^^^^^ meta.tag.other.surface 78 | {#case @value} 79 | # ^^^^^ variable.other.constant.elixir 80 | # ^^^^ keyword.control.conditional.surface 81 | # ^ punctuation.section.embedded.end.surface 82 | # ^^punctuation.section.embedded.begin.surface 83 | # ^^^^^^^^^^^^^^ meta.embedded.surface 84 | {#match [{_, first} | _]} 85 | # ^ punctuation.section.embedded.end.surface 86 | # ^ punctuation.section.sequence.end.elixir 87 | # ^^^^^ variable.parameter.elixir 88 | # ^ punctuation.section.sequence.begin.elixir 89 | # ^^ punctuation.section.embedded.begin.surface 90 | First {first} 91 | # ^ punctuation.section.embedded.end.elixir - source 92 | # ^^^^^ source.elixir.embedded.html variable.other.elixir 93 | # ^ punctuation.section.embedded.begin.elixir - source 94 | # ^^^^^^^ meta.embedded.surface 95 | {#match []} 96 | # ^^ meta.brackets.elixir 97 | Value is empty 98 | {#match _} 99 | # ^ variable.parameter.unused.elixir 100 | Value is something else 101 | {/case} 102 | 103 | # ^ punctuation.definition.tag.end.html 104 | # ^ - punctuation.definition.tag.end.html 105 | # ^^^^^^^^^ entity.name.tag.end.surface 106 | # ^ punctuation.accessor.dot.surface 107 | # ^^^ entity.name.tag.end.surface 108 | # ^ punctuation.accessor.dot.surface 109 | # ^^^^ entity.name.tag.end.surface 110 | #^^^^^^^^^^^^^^^^^^^^^ meta.tag.other.surface 111 | 112 | 113 | # ^ punctuation.section.embedded.end.elixir 114 | # ^^^^ variable.other.constant.elixir 115 | # ^ keyword.operator.attribute.elixir 116 | # ^^^^^ source.elixir.embedded.html 117 | # ^ punctuation.section.embedded.begin.elixir 118 | # ^ punctuation.separator.key-value.html 119 | # ^^^^ entity.other.attribute-name.html 120 | # ^ punctuation.section.embedded.end.elixir 121 | # ^^^^ variable.other.constant.elixir 122 | # ^ keyword.operator.attribute.elixir 123 | # ^ keyword.operator.match.elixir 124 | # ^^^^^^ source.elixir.embedded.html 125 | # ^ punctuation.section.embedded.begin.elixir 126 | 127 | 128 | <#slot :args={value: @value, max: @max} /> 129 | # ^ punctuation.section.embedded.end.elixir 130 | # ^^^^^^^^^^^^^^^^^^^^^^^^ source.elixir.embedded.html 131 | # ^ punctuation.section.embedded.begin.elixir 132 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.attribute-with-value.html meta.embedded.surface - string 133 | # ^ punctuation.separator.key-value.html 134 | # ^^^^ entity.other.attribute-name.surface 135 | # ^ punctuation.definition.attribute.begin.surface 136 | # ^^^^^^ meta.tag.other.surface meta.attribute-with-value.html - meta.string - meta.embedded 137 | #^^^^^ entity.name.tag.begin.surface 138 | #^^^^^^ meta.tag.other.surface - meta.attribute-with-value 139 | 140 | <:slot> 141 | # ^^^^^ entity.name.tag.end.surface 142 | #^^^^^ entity.name.tag.begin.surface 143 | #^^^^^^^^^^^^^^ meta.tag.other.surface - meta.attribute-with-value 144 | 145 | <#Raw> 146 | # ^ punctuation.definition.tag.end.html 147 | # ^^^^ entity.name.tag.begin.surface 148 | # ^ punctuation.definition.tag.begin.html 149 | # ^^^^^^ meta.tag.other.surface - meta.attribute-with-value 150 | <#Raw> 151 | # ^ - punctuation 152 | # ^^^^ - entity 153 | # ^ - punctuation 154 | <:slot args={@args}> 155 | # ^^^^^ - entity 156 | # ^^^^^^^^^^^^ - variable - entity - punctuation 157 | # ^^^^^ - entity 158 | 159 | # ^^^^ entity.name.tag.inline.any.html 160 | # ^^^^^^^^ - source.elixir 161 | # ^^^^^^^^ - source.elixir 162 | # ^^^^^^^^ - source.elixir 163 | # ^^^^ entity.name.tag.inline.any.html 164 | 165 | # ^ punctuation.definition.tag.end.html 166 | # ^^^^ entity.name.tag.end.surface 167 | # ^^ punctuation.definition.tag.begin.html 168 | # ^^^^^^^ meta.tag.other.surface - meta.attribute-with-value 169 | 170 | <#Markdown class="content" opts={x: "y"}> 171 | # ^ punctuation.definition.tag.end.html 172 | # ^ meta.tag.other.surface - meta.attribute-with-value 173 | # ^ punctuation.section.embedded.end.elixir - source 174 | # ^^^^^^ source.elixir.embedded.html 175 | # ^ punctuation.section.embedded.begin.elixir - source 176 | # ^^^^^^^^^^^^^ meta.tag.other.surface meta.attribute-with-value.html 177 | # ^ meta.tag.other.surface - meta.attribute-with-value 178 | # ^^^^^^^^^ string.quoted.double.html 179 | # ^^^^^^^^^^^^^^^ meta.tag.other.surface meta.attribute-with-value.class.html 180 | # ^^^^^ entity.other.attribute-name.class.html 181 | # ^^^^^^^^^ entity.name.tag.begin.surface 182 | # ^ punctuation.definition.tag.begin.html 183 | # ^^^^^^^^^^^ meta.tag.other.surface - meta.attribute-with-value 184 | # Markdown 185 | <#Markdown> 186 | #^^^^^^^^^ - entity.name.tag.end.surface 187 | 188 | # ^^^^^^^^^ entity.name.tag.end.surface 189 | #^^^^^^^^^^^ meta.tag.other.surface 190 | # ^ - meta.tag 191 | 192 | # ^^^^ entity.name.tag.inline.any.html 193 | # ^^^^ entity.name.tag.inline.any.html 194 | 195 | # ^ - meta.tag 196 | # ^ punctuation.definition.tag.end.html 197 | # ^^^^^^^^^ entity.name.tag.end.surface 198 | # ^^ punctuation.definition.tag.begin.surface 199 | # ^^^^^^^^^^^^ meta.tag.other.surface 200 | 201 | 207 | 208 | 212 | 213 | 214 | # ^^^^^^^^^ - entity 215 | # ^^^^^^^^^ - entity 216 | 217 | # ^ - punctuation.section.embedded.end.elixir - source 218 | # ^^^^^^^ - source.elixir.embedded.html variable.other.elixir 219 | # ^ - punctuation.section.embedded.begin.elixir - source 220 | # ^^^^^^^^^ - meta.embedded.surface 221 | #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block.html 222 | {!-- This {comment} won't be sent to the browser --} 223 | # ^^^ punctuation.definition.comment.end.surface 224 | #^^^^ punctuation.definition.comment.begin.surface 225 | #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ text.html.surface meta.embedded.surface comment.block.surface - source - variable 226 | 227 | 228 | # ^^^^ - source.elixir 229 | #^^^^^^^ entity.name.tag.begin.html 230 | 231 | <{}}>{@x} 232 | # ^^^ entity.name.tag.end.html 233 | # ^^ source.elixir.embedded.html 234 | #^^^ entity.name.tag.begin.html 235 | <...>{@x} 236 | # ^^^ entity.name.tag.end.html 237 | # ^^ source.elixir.embedded.html 238 | #^^^ entity.name.tag.begin.html 239 | <{x}>{@x} 240 | # ^^^ entity.name.tag.end.html - source.elixir 241 | # ^ variable.other.constant.elixir 242 | # ^^ source.elixir.embedded.html 243 | # ^ keyword.operator.attribute.elixir 244 | #^^^ entity.name.tag.begin.html - source.elixir 245 | 246 | <.not_a_func> 247 | #^^^^^^^^^^^ entity.name.tag.begin.html - variable.function 248 | 249 | # ^^^^^^^^^^^ entity.name.tag.end.html - variable.function 250 | 251 | <.table rows={@users}> 252 | # ^^^^^^ entity.name.tag.end.html 253 | # ^^^^^^^^^ meta.tag.other.surface 254 | # ^^^^^^^^ meta.tag.other.surface meta.attribute-with-value.html meta.embedded.surface 255 | # ^^^^^ meta.tag.other.surface meta.attribute-with-value.html - meta.embedded 256 | #^^^^^^ meta.tag.other.surface entity.name.tag.begin.html 257 | --------------------------------------------------------------------------------