├── .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: (?)\s*(<%(?:%?=?))
71 | captures:
72 | 1: punctuation.definition.tag.begin.html
73 | 2: punctuation.section.embedded.begin.eex
74 | push: [tag-other-content, eex-embedded-content-pop]
75 |
76 | eex-embedded-attribute-name:
77 | - match: \s*(<%(?:%?=?))
78 | captures:
79 | 1: punctuation.section.embedded.begin.eex
80 | push:
81 | - tag-generic-attribute-meta
82 | - tag-generic-attribute-assignment
83 | - eex-embedded-content-pop
84 |
--------------------------------------------------------------------------------
/syntaxes/HTML (HEEx).sublime-syntax:
--------------------------------------------------------------------------------
1 | %YAML 1.2
2 | ---
3 | version: 2
4 | name: HTML (HEEx)
5 | scope: text.html.heex
6 | extends: Packages/HTML/HTML.sublime-syntax
7 |
8 | file_extensions: [heex]
9 | authors:
10 | - Aziz Köksal
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: {{is_heex_tag_name}}
67 | scope: punctuation.definition.tag.begin.html
68 | push: [heex-end-tag-content-pop, heex-end-tag-name-pop]
69 | - include: elixir-embedded
70 |
71 | heex-begin-tag-content-pop:
72 | - meta_scope: meta.tag.other.heex
73 | - include: tag-end-maybe-self-closing
74 | - include: tag-attributes
75 |
76 | heex-end-tag-content-pop:
77 | - meta_scope: meta.tag.other.heex
78 | - include: tag-end
79 | - include: else-pop
80 |
81 | heex-begin-tag-name-pop:
82 | - match: '{{heex_tag_name}}|{{heex_slot_name}}'
83 | scope: entity.name.tag.begin.heex
84 | - include: heex-tag-function-name-pop
85 |
86 | heex-end-tag-name-pop:
87 | - match: '{{heex_tag_name}}|{{heex_slot_name}}'
88 | scope: entity.name.tag.end.heex
89 | - include: heex-tag-function-name-pop
90 |
91 | heex-tag-function-name-pop:
92 | - match: ({{heex_tag_char}}+)|(\.)
93 | captures:
94 | 1: variable.function.heex
95 | 2: punctuation.accessor.dot.heex
96 | - include: immediately-pop
97 |
98 | heex-phx-attributes:
99 | # https://hexdocs.pm/phoenix_live_view/bindings.html
100 | - match: |-
101 | (?x) phx- (?>
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>#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: ()(#Markdown)\b
80 | captures:
81 | 1: punctuation.definition.tag.begin.html
82 | 2: entity.name.tag.end.surface
83 | set: surface-end-tag-content-pop
84 |
85 | # # NB: commented out because Markdown is not really highlighted correctly
86 | # # due to the surrounding indentation.
87 | # - match: ''
88 | # embed: scope:text.html.markdown
89 | # escape: (?=#Markdown\b)
90 |
91 | # At least highlight basic HTML.
92 | - include: scope:text.html.basic
93 | apply_prototype: true
94 |
95 | surface-raw-tag:
96 | # NB: the #Raw-tag cannot be nested, e.g.: `<#Raw><#Raw>#Raw>#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: ()(#Raw)\b
107 | captures:
108 | 1: punctuation.definition.tag.begin.html
109 | 2: entity.name.tag.end.surface
110 | set: surface-end-tag-content-pop
111 |
112 | - include: scope:text.html.basic
113 | apply_prototype: true
114 |
115 | surface-other-tags:
116 | - match: (<)([#:]{{surface_tag_name}}|{{surface_upcase_tag_name}})
117 | captures:
118 | 1: punctuation.definition.tag.begin.surface
119 | 2: entity.name.tag.begin.surface
120 | push: [surface-begin-tag-content-pop, surface-begin-tag-name-pop]
121 |
122 | - match: ()([#:]{{surface_tag_name}}|{{surface_upcase_tag_name}})
123 | captures:
124 | 1: punctuation.definition.tag.begin.surface
125 | 2: entity.name.tag.end.surface
126 | push: [surface-end-tag-content-pop, surface-end-tag-name-pop]
127 |
128 | html-custom-tags:
129 | - match: (<)({{html_custom_tag_char}}+)
130 | captures:
131 | 1: punctuation.definition.tag.begin.html
132 | 2: entity.name.tag.begin.html
133 | push: surface-begin-tag-content-pop
134 |
135 | - match: ()({{html_custom_tag_char}}+)
136 | captures:
137 | 1: punctuation.definition.tag.begin.html
138 | 2: entity.name.tag.end.html
139 | push: surface-end-tag-content-pop
140 |
141 | surface-begin-tag-content-pop:
142 | - meta_scope: meta.tag.other.surface
143 | - include: tag-end-maybe-self-closing
144 | - include: tag-attributes
145 |
146 | surface-end-tag-content-pop:
147 | - meta_scope: meta.tag.other.surface
148 | - include: tag-end
149 | - include: else-pop
150 |
151 | surface-begin-tag-name-pop:
152 | - match: '{{surface_upcase_tag_name}}'
153 | scope: entity.name.tag.begin.surface
154 | - include: surface-tag-name-common-pop
155 |
156 | surface-end-tag-name-pop:
157 | - match: '{{surface_upcase_tag_name}}'
158 | scope: entity.name.tag.end.surface
159 | - include: surface-tag-name-common-pop
160 |
161 | surface-tag-name-common-pop:
162 | - match: '[[:lower:]_]\w*[?!]?'
163 | scope: variable.function.surface
164 | pop: 1
165 | - match: \.
166 | scope: punctuation.accessor.dot.surface
167 | - include: immediately-pop
168 |
169 | surface-attributes:
170 | - match: |-
171 | (?x)
172 | (:)(
173 | if | hook | show | let | args | values
174 | | on-(?:
175 | click | capture-click | blur | focus | change | submit | keydown | keyup
176 | | window-(?:focus | blur | keydown | keyup)
177 | )
178 | ){{attribute_name_break}}
179 | captures:
180 | 1: punctuation.definition.attribute.begin.surface
181 | 2: entity.other.attribute-name.surface
182 | push: [tag-generic-attribute-meta, tag-generic-attribute-assignment]
183 |
184 | surface-blocks:
185 | - match: ({/)(?:(case|elseif|else|if|unless)|(for)|([a-z]+))(})
186 | captures:
187 | 0: meta.embedded.surface
188 | 1: punctuation.section.embedded.begin.surface
189 | 2: keyword.control.conditional.surface
190 | 3: keyword.control.loop.surface
191 | 4: keyword.control.surface
192 | 5: punctuation.section.embedded.end.surface
193 | - match: ({#)(match)\b
194 | captures:
195 | 1: punctuation.section.embedded.begin.surface
196 | 2: keyword.control.conditional.surface
197 | push: surface-match-block-content-pop
198 | - match: ({#)(?:(case|elseif|else|if|unless)|(for)|([a-z]+))\b
199 | captures:
200 | 1: punctuation.section.embedded.begin.surface
201 | 2: keyword.control.conditional.surface
202 | 3: keyword.control.loop.surface
203 | 4: keyword.control.surface
204 | push: surface-conditional-block-content-pop
205 |
206 | surface-conditional-block-content-pop:
207 | - meta_scope: meta.embedded.surface
208 | - meta_content_scope: source.elixir.embedded.html
209 | - include: surface-block-end-pop
210 | - include: scope:source.elixir#arguments_ws
211 | apply_prototype: true
212 |
213 | surface-match-block-content-pop:
214 | - meta_scope: meta.embedded.surface
215 | - meta_content_scope: source.elixir.embedded.html
216 | - include: surface-block-end-pop
217 | - include: scope:source.elixir#parameters
218 | apply_prototype: true
219 |
220 | surface-block-end-pop:
221 | - match: \}
222 | scope: punctuation.section.embedded.end.surface
223 | pop: 1
224 |
225 | surface-comment:
226 | - match: '{!--'
227 | scope: punctuation.definition.comment.begin.surface
228 | push: surface-comment-content-pop
229 |
230 | surface-comment-content-pop:
231 | - meta_scope: meta.embedded.surface comment.block.surface
232 | - match: --}
233 | scope: punctuation.definition.comment.end.surface
234 | pop: 1
235 |
236 | # Elixir:
237 |
238 | elixir-embedded:
239 | - match: (?={)
240 | push: elixir-embedded-pop
241 |
242 | elixir-embedded-pop:
243 | - match: \{
244 | scope: punctuation.section.embedded.begin.elixir
245 | set: elixir-embedded-content-pop
246 |
247 | elixir-embedded-content-pop:
248 | - meta_scope: meta.embedded.surface
249 | - meta_content_scope: source.elixir.embedded.html
250 | - include: HTML (HEEx).sublime-syntax#elixir-embedded-content-nometa-pop
251 |
--------------------------------------------------------------------------------
/syntaxes/SQL (Elixir).sublime-syntax:
--------------------------------------------------------------------------------
1 | %YAML 1.2
2 | ---
3 | name: SQL (Elixir)
4 | scope: source.ex.sql
5 |
6 | file_extensions: [ex.sql]
7 | authors: [Aziz Köksal ]
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 | <%= @tag %>>
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | < <%= @tag %> <%= @attr %>>
80 |
81 |
82 | <%= @tag %>>
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 | Show
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 | #Raw>
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 | #Markdown>
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 | #Markdown>
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}{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 |
--------------------------------------------------------------------------------