├── .coffeelintignore ├── .github ├── no-response.yml └── workflows │ └── ci.yml ├── .gitignore ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── coffeelint.json ├── grammars ├── mustache.cson └── sql with mustaches.cson ├── package.json └── spec └── mustache-spec.coffee /.coffeelintignore: -------------------------------------------------------------------------------- 1 | spec/fixtures 2 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an issue is closed for lack of response 4 | daysUntilClose: 28 5 | 6 | # Label requiring a response 7 | responseRequiredLabel: more-information-needed 8 | 9 | # Comment to post when closing an issue for lack of response. Set to `false` to disable. 10 | closeComment: > 11 | This issue has been automatically closed because there has been no response 12 | to our request for more information from the original author. With only the 13 | information that is currently in the issue, we don't have enough information 14 | to take action. Please reach out if you have or find the answers we need so 15 | that we can investigate further. 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | env: 6 | CI: true 7 | 8 | jobs: 9 | Test: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | channel: [stable, beta] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v1 17 | - uses: UziTech/action-setup-atom@v2 18 | with: 19 | version: ${{ matrix.channel }} 20 | - name: Install dependencies 21 | run: apm install 22 | - name: Run tests 23 | run: atom --test spec 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ### Prerequisites 10 | 11 | * [ ] Put an X between the brackets on this line if you have done all of the following: 12 | * Reproduced the problem in Safe Mode: http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode 13 | * Followed all applicable steps in the debugging guide: http://flight-manual.atom.io/hacking-atom/sections/debugging/ 14 | * Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq 15 | * Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom 16 | * Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages 17 | 18 | ### Description 19 | 20 | [Description of the issue] 21 | 22 | ### Steps to Reproduce 23 | 24 | 1. [First Step] 25 | 2. [Second Step] 26 | 3. [and so on...] 27 | 28 | **Expected behavior:** [What you expect to happen] 29 | 30 | **Actual behavior:** [What actually happens] 31 | 32 | **Reproduces how often:** [What percentage of the time does it reproduce?] 33 | 34 | ### Versions 35 | 36 | You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running. 37 | 38 | ### Additional Information 39 | 40 | Any additional information, configuration or data that might be necessary to reproduce the issue. 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 GitHub Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Requirements 2 | 3 | * Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. 4 | * All new code requires tests to ensure against regressions 5 | 6 | ### Description of the Change 7 | 8 | 13 | 14 | ### Alternate Designs 15 | 16 | 17 | 18 | ### Benefits 19 | 20 | 21 | 22 | ### Possible Drawbacks 23 | 24 | 25 | 26 | ### Applicable Issues 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##### Atom and all repositories under Atom will be archived on December 15, 2022. Learn more in our [official announcement](https://github.blog/2022-06-08-sunsetting-atom/) 2 | # Mustache/Handlebars support in Atom 3 | [![OS X Build Status](https://travis-ci.org/atom/language-mustache.svg?branch=master)](https://travis-ci.org/atom/language-mustache) 4 | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/mbxnxaojqp0g7ldv/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/language-mustache/branch/master) 5 | [![Dependency Status](https://david-dm.org/atom/language-mustache.svg)](https://david-dm.org/atom/language-mustache) 6 | 7 | Adds syntax highlighting to [Mustache](http://mustache.github.io) and [Handlebars](http://handlebarsjs.com) files in Atom. 8 | 9 | Contributions are greatly appreciated. Please fork this repository and open a pull request to add snippets, make grammar tweaks, etc. 10 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "max_line_length": { 3 | "level": "ignore" 4 | }, 5 | "no_empty_param_list": { 6 | "level": "error" 7 | }, 8 | "arrow_spacing": { 9 | "level": "error" 10 | }, 11 | "no_interpolation_in_single_quotes": { 12 | "level": "error" 13 | }, 14 | "no_debugger": { 15 | "level": "error" 16 | }, 17 | "prefer_english_operator": { 18 | "level": "error" 19 | }, 20 | "colon_assignment_spacing": { 21 | "spacing": { 22 | "left": 0, 23 | "right": 1 24 | }, 25 | "level": "error" 26 | }, 27 | "braces_spacing": { 28 | "spaces": 0, 29 | "level": "error" 30 | }, 31 | "spacing_after_comma": { 32 | "level": "error" 33 | }, 34 | "no_stand_alone_at": { 35 | "level": "error" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /grammars/mustache.cson: -------------------------------------------------------------------------------- 1 | 'scopeName': 'text.html.mustache' 2 | 'name': 'HTML (Mustache)' 3 | 'fileTypes': [ 4 | 'handlebars' 5 | 'hbs' 6 | 'hjs' 7 | 'mst' 8 | 'mu' 9 | 'mustache' 10 | 'rac' 11 | 'ractive' 12 | 'stache' 13 | ] 14 | 'injections': 15 | 'L:text.html.mustache - (meta.tag.template.mustache | comment.block)': 16 | 'patterns': [ 17 | { 18 | # This should be the only pattern here! 19 | # Add everything else into #mustache below 20 | 'include': '#mustache' 21 | } 22 | ] 23 | 'patterns': [ 24 | { 25 | 'include': 'text.html.basic' 26 | } 27 | ] 28 | 'repository': 29 | # The master pattern. All new patterns should be added here instead of in 30 | # 'injections' to make including this language easier. 31 | # See https://github.com/atom/language-mustache/issues/26 32 | 'mustache': 33 | 'patterns': [ 34 | { 35 | 'include': '#block-comment' 36 | } 37 | { 38 | 'include': '#inline-comment' 39 | } 40 | { 41 | 'include': '#block-expression-start' 42 | } 43 | { 44 | 'include': '#block-expression-end' 45 | } 46 | { 47 | 'include': '#escape' 48 | } 49 | { 50 | 'include': '#template' 51 | } 52 | ] 53 | 'block-comment': 54 | 'begin': '{{!--' 55 | 'beginCaptures': 56 | '0': 57 | 'name': 'punctuation.definition.comment.mustache' 58 | 'end': '--}}' 59 | 'endCaptures': 60 | '0': 61 | 'name': 'punctuation.definition.comment.mustache' 62 | 'name': 'comment.block.mustache' 63 | 'inline-comment': 64 | 'begin': '{{!' 65 | 'beginCaptures': 66 | '0': 67 | 'name': 'punctuation.definition.comment.mustache' 68 | 'end': '}}' 69 | 'endCaptures': 70 | '0': 71 | 'name': 'punctuation.definition.comment.mustache' 72 | 'name': 'comment.block.mustache' 73 | 'block-expression-start': 74 | 'begin': '{{([#^])\\s*([\\w\\.]*)' 75 | 'beginCaptures': 76 | '0': 77 | 'name': 'entity.name.tag.mustache' 78 | '1': 79 | 'name': 'punctuation.definition.block.begin.mustache' 80 | '2': 81 | 'name': 'entity.name.function.mustache' 82 | 'end': '\\s*(}})' 83 | 'endCaptures': 84 | '1': 85 | 'name': 'entity.name.tag.mustache' 86 | 'name': 'meta.tag.template.mustache' 87 | 'block-expression-end': 88 | 'begin': '{{(/)\\s*([\\w\\.]*)' 89 | 'beginCaptures': 90 | '0': 91 | 'name': 'entity.name.tag.mustache' 92 | '1': 93 | 'name': 'punctuation.definition.block.end.mustache' 94 | '2': 95 | 'name': 'entity.name.function.mustache' 96 | 'end': '\\s*(}})' 97 | 'endCaptures': 98 | '1': 99 | 'name': 'entity.name.tag.mustache' 100 | 'name': 'meta.tag.template.mustache' 101 | 'escape': 102 | 'begin': '{{{' 103 | 'end': '}}}' 104 | 'captures': 105 | '0': 106 | 'name': 'entity.name.tag.mustache' 107 | 'name': 'meta.tag.template.raw.mustache' 108 | 'template': 109 | 'begin': '{{[<>]?' 110 | 'end': '}}' 111 | 'captures': 112 | '0': 113 | 'name': 'entity.name.tag.mustache' 114 | 'name': 'meta.tag.template.mustache' 115 | -------------------------------------------------------------------------------- /grammars/sql with mustaches.cson: -------------------------------------------------------------------------------- 1 | 'scopeName': 'source.sql.mustache' 2 | 'name': 'SQL (Mustache)' 3 | 'fileTypes': [ 4 | ] 5 | 'patterns': [ 6 | { 7 | 'include': 'text.html.mustache#mustache' 8 | } 9 | { 10 | 'include': 'source.sql' 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "language-mustache", 3 | "version": "0.14.5", 4 | "description": "Mustache language support in Atom", 5 | "license": "MIT", 6 | "engines": { 7 | "atom": "*", 8 | "node": "*" 9 | }, 10 | "homepage": "http://atom.github.io/language-mustache", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/atom/language-mustache.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/atom/language-mustache/issues" 17 | }, 18 | "devDependencies": { 19 | "coffeelint": "^1.10.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spec/mustache-spec.coffee: -------------------------------------------------------------------------------- 1 | describe 'Mustache grammar', -> 2 | grammar = null 3 | 4 | beforeEach -> 5 | waitsForPromise -> 6 | atom.packages.activatePackage('language-html') 7 | 8 | waitsForPromise -> 9 | atom.packages.activatePackage('language-mustache') 10 | 11 | runs -> 12 | grammar = atom.grammars.grammarForScopeName('text.html.mustache') 13 | 14 | it 'parses the grammar', -> 15 | expect(grammar).toBeTruthy() 16 | expect(grammar.scopeName).toBe 'text.html.mustache' 17 | 18 | it 'parses expressions', -> 19 | {tokens} = grammar.tokenizeLine("{{name}}") 20 | 21 | expect(tokens[0]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 22 | expect(tokens[1]).toEqual value: 'name', scopes: ['text.html.mustache', 'meta.tag.template.mustache'] 23 | expect(tokens[2]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 24 | 25 | it 'parses expressions in HTML attributes', -> 26 | {tokens} = grammar.tokenizeLine("") 27 | 28 | expect(tokens[6]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.inline.a.html', 'meta.attribute-with-value.html', 'string.quoted.single.html', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 29 | expect(tokens[8]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.inline.a.html', 'meta.attribute-with-value.html', 'string.quoted.single.html', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 30 | expect(tokens[9]).toEqual value: "'", scopes: ['text.html.mustache', 'meta.tag.inline.a.html', 'meta.attribute-with-value.html', 'string.quoted.single.html', 'punctuation.definition.string.end.html'] 31 | 32 | it 'parses block comments', -> 33 | {tokens} = grammar.tokenizeLine("{{!--{{comment}}--}}") 34 | 35 | expect(tokens[0]).toEqual value: '{{!--', scopes: ['text.html.mustache', 'comment.block.mustache', 'punctuation.definition.comment.mustache'] 36 | expect(tokens[1]).toEqual value: '{{comment}}', scopes: ['text.html.mustache', 'comment.block.mustache'] 37 | expect(tokens[2]).toEqual value: '--}}', scopes: ['text.html.mustache', 'comment.block.mustache', 'punctuation.definition.comment.mustache'] 38 | 39 | it 'parses comments', -> 40 | {tokens} = grammar.tokenizeLine("{{!comment}}") 41 | 42 | expect(tokens[0]).toEqual value: '{{!', scopes: ['text.html.mustache', 'comment.block.mustache', 'punctuation.definition.comment.mustache'] 43 | expect(tokens[1]).toEqual value: 'comment', scopes: ['text.html.mustache', 'comment.block.mustache'] 44 | expect(tokens[2]).toEqual value: '}}', scopes: ['text.html.mustache', 'comment.block.mustache', 'punctuation.definition.comment.mustache'] 45 | 46 | it 'parses block expression', -> 47 | {tokens} = grammar.tokenizeLine("{{#each people}}") 48 | 49 | expect(tokens[0]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 50 | expect(tokens[1]).toEqual value: '#', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'punctuation.definition.block.begin.mustache'] 51 | expect(tokens[2]).toEqual value: 'each', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'entity.name.function.mustache'] 52 | expect(tokens[3]).toEqual value: ' people', scopes: ['text.html.mustache', 'meta.tag.template.mustache'] 53 | expect(tokens[4]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 54 | 55 | {tokens} = grammar.tokenizeLine("{{# nested.block }}") 56 | 57 | expect(tokens[0]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 58 | expect(tokens[1]).toEqual value: '#', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'punctuation.definition.block.begin.mustache'] 59 | expect(tokens[3]).toEqual value: 'nested.block', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'entity.name.function.mustache'] 60 | expect(tokens[5]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 61 | 62 | {tokens} = grammar.tokenizeLine("{{^repo}}") 63 | 64 | expect(tokens[0]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 65 | expect(tokens[1]).toEqual value: '^', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'punctuation.definition.block.begin.mustache'] 66 | expect(tokens[2]).toEqual value: 'repo', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'entity.name.function.mustache'] 67 | expect(tokens[3]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 68 | 69 | {tokens} = grammar.tokenizeLine("{{/if}}") 70 | 71 | expect(tokens[0]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 72 | expect(tokens[1]).toEqual value: '/', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'punctuation.definition.block.end.mustache'] 73 | expect(tokens[2]).toEqual value: 'if', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache', 'entity.name.function.mustache'] 74 | expect(tokens[3]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 75 | 76 | it 'parses unescaped expressions', -> 77 | {tokens} = grammar.tokenizeLine("{{{do not escape me}}}") 78 | 79 | expect(tokens[0]).toEqual value: '{{{', scopes: ['text.html.mustache', 'meta.tag.template.raw.mustache', 'entity.name.tag.mustache'] 80 | expect(tokens[1]).toEqual value: 'do not escape me', scopes: ['text.html.mustache', 'meta.tag.template.raw.mustache'] 81 | expect(tokens[2]).toEqual value: '}}}', scopes: ['text.html.mustache', 'meta.tag.template.raw.mustache', 'entity.name.tag.mustache'] 82 | 83 | it 'does not tokenize tags within tags', -> 84 | {tokens} = grammar.tokenizeLine("{{test{{test}}}}") 85 | 86 | expect(tokens[0]).toEqual value: '{{', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 87 | expect(tokens[1]).toEqual value: 'test{{test', scopes: ['text.html.mustache', 'meta.tag.template.mustache'] 88 | expect(tokens[2]).toEqual value: '}}', scopes: ['text.html.mustache', 'meta.tag.template.mustache', 'entity.name.tag.mustache'] 89 | expect(tokens[3]).toEqual value: '}}', scopes: ['text.html.mustache'] 90 | 91 | it 'does not tokenize comments within comments', -> 92 | {tokens} = grammar.tokenizeLine("{{!test{{!test}}}}") 93 | 94 | expect(tokens[0]).toEqual value: '{{!', scopes: ['text.html.mustache', 'comment.block.mustache', 'punctuation.definition.comment.mustache'] 95 | expect(tokens[1]).toEqual value: 'test{{!test', scopes: ['text.html.mustache', 'comment.block.mustache'] 96 | expect(tokens[2]).toEqual value: '}}', scopes: ['text.html.mustache', 'comment.block.mustache', 'punctuation.definition.comment.mustache'] 97 | expect(tokens[3]).toEqual value: '}}', scopes: ['text.html.mustache'] 98 | 99 | it 'does not tokenize Mustache expressions inside HTML comments', -> 100 | {tokens} = grammar.tokenizeLine("") 101 | 102 | expect(tokens[0]).toEqual value: '', scopes: ['text.html.mustache', 'comment.block.html', 'punctuation.definition.comment.html'] 105 | --------------------------------------------------------------------------------