26 |28 | 29 | Add an `sql` macro/function to your project to enjoy SQL highlighting anywhere it's used. 30 | 31 |27 |
32 |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 |33 |
40 |42 | 43 | ### The YAML `~y` and `~Y` sigils ([`YamlElixir`](https://hexdocs.pm/yaml_elixir/YamlElixir.Sigil.html#sigil_y/2)) 44 | 45 |41 |
46 |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 | -------------------------------------------------------------------------------- /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 |47 |
([^<]*)
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 | -------------------------------------------------------------------------------- /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_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 |
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 |