├── .coffeelintignore ├── .github ├── no-response.yml └── workflows │ ├── ci.yml │ └── codeql.yml ├── .gitignore ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── coffeelint.json ├── lib ├── autocomplete-snippets.js └── snippets-provider.js ├── package.json └── spec ├── autocomplete-snippets-spec.js └── fixtures └── sample.js /.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 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "Code Scanning - Action" 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '0 0 * * 0' 7 | 8 | jobs: 9 | CodeQL-Build: 10 | 11 | strategy: 12 | fail-fast: false 13 | 14 | 15 | # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v2 21 | 22 | # Initializes the CodeQL tools for scanning. 23 | - name: Initialize CodeQL 24 | uses: github/codeql-action/init@v1 25 | # Override language selection by uncommenting this and choosing your languages 26 | # with: 27 | # languages: go, javascript, csharp, python, cpp, java 28 | 29 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 30 | # If this step fails, then you should remove it and run the build manually (see below). 31 | - name: Autobuild 32 | uses: github/codeql-action/autobuild@v1 33 | 34 | # ℹ️ Command-line programs to run using the OS shell. 35 | # 📚 https://git.io/JvXDl 36 | 37 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 38 | # and modify them (or add more) to build your code if your project 39 | # uses a compiled language 40 | 41 | #- run: | 42 | # make bootstrap 43 | # make release 44 | 45 | - name: Perform CodeQL Analysis 46 | uses: github/codeql-action/analyze@v1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /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 FILSH Media GmbH 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 | # autocomplete+ snippet suggestions package 3 | [![OS X Build Status](https://travis-ci.org/atom/autocomplete-snippets.svg)](https://travis-ci.org/atom/autocomplete-snippets) 4 | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/72kfi83l6cw90joy/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-snippets/branch/master) [![Dependency Status](https://david-dm.org/atom/autocomplete-snippets.svg)](https://david-dm.org/atom/autocomplete-snippets) 5 | 6 | Adds snippets to autocomplete+ suggestions 7 | 8 | ## Features 9 | 10 | * Adds user snippets and language snippets to the autocomplete+ suggestions list 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/autocomplete-snippets.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | provider: null, 3 | 4 | activate() {}, 5 | 6 | deactivate() { 7 | this.provider = null 8 | }, 9 | 10 | provide() { 11 | if (this.provider == null) { 12 | const SnippetsProvider = require('./snippets-provider') 13 | this.provider = new SnippetsProvider() 14 | if (this.snippets != null) { 15 | this.provider.setSnippetsSource(this.snippets) 16 | } 17 | } 18 | 19 | return this.provider 20 | }, 21 | 22 | consumeSnippets(snippets) { 23 | this.snippets = snippets 24 | return (this.provider != null ? this.provider.setSnippetsSource(this.snippets) : undefined) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/snippets-provider.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | class SnippetsProvider { 3 | constructor() { 4 | this.selector = '*' 5 | this.inclusionPriority = 1 6 | this.suggestionPriority = 2 7 | this.filterSuggestions = true 8 | 9 | this.showIcon = true 10 | this.snippetsSource = { 11 | snippetsForScopes(scopeDescriptor) { 12 | return atom.config.get('snippets', {scope: scopeDescriptor}) 13 | } 14 | } 15 | } 16 | 17 | setSnippetsSource(snippetsSource) { 18 | if (typeof (snippetsSource != null ? snippetsSource.snippetsForScopes : undefined) === "function") { 19 | return this.snippetsSource = snippetsSource 20 | } 21 | } 22 | 23 | getSuggestions({scopeDescriptor, prefix}) { 24 | if (!(prefix != null ? prefix.length : undefined)) { return } 25 | const scopeSnippets = this.snippetsSource.snippetsForScopes(scopeDescriptor) 26 | return this.findSuggestionsForPrefix(scopeSnippets, prefix) 27 | } 28 | 29 | findSuggestionsForPrefix(snippets, prefix) { 30 | if (snippets == null) { return [] } 31 | 32 | const suggestions = [] 33 | for (let snippetPrefix in snippets) { 34 | const snippet = snippets[snippetPrefix] 35 | if (!snippet || !snippetPrefix || !prefix || !firstCharsEqual(snippetPrefix, prefix)) { continue } 36 | suggestions.push({ 37 | iconHTML: this.showIcon ? undefined : false, 38 | type: 'snippet', 39 | text: snippet.prefix, 40 | replacementPrefix: prefix, 41 | rightLabel: snippet.name, 42 | rightLabelHTML: snippet.rightLabelHTML, 43 | leftLabel: snippet.leftLabel, 44 | leftLabelHTML: snippet.leftLabelHTML, 45 | description: snippet.description, 46 | descriptionMoreURL: snippet.descriptionMoreURL 47 | }) 48 | } 49 | 50 | suggestions.sort(ascendingPrefixComparator) 51 | return suggestions 52 | } 53 | 54 | onDidInsertSuggestion({editor}) { 55 | return atom.commands.dispatch(atom.views.getView(editor), 'snippets:expand') 56 | } 57 | } 58 | 59 | const ascendingPrefixComparator = (a, b) => a.text.localeCompare(b.text) 60 | 61 | const firstCharsEqual = (str1, str2) => str1[0].toLowerCase() === str2[0].toLowerCase() 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autocomplete-snippets", 3 | "main": "./lib/autocomplete-snippets", 4 | "version": "1.12.1", 5 | "description": "Adds snippets to autocomplete+ suggestions", 6 | "repository": "https://github.com/atom/autocomplete-snippets", 7 | "license": "MIT", 8 | "engines": { 9 | "atom": ">=0.174.0 <2.0.0" 10 | }, 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "coffeelint": "^1.9.7" 14 | }, 15 | "providedServices": { 16 | "autocomplete.provider": { 17 | "versions": { 18 | "2.0.0": "provide" 19 | } 20 | } 21 | }, 22 | "consumedServices": { 23 | "snippets": { 24 | "versions": { 25 | "0.1.0": "consumeSnippets" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spec/autocomplete-snippets-spec.js: -------------------------------------------------------------------------------- 1 | describe('AutocompleteSnippets', () => { 2 | let [completionDelay, editor, editorView] = [] 3 | 4 | beforeEach(() => { 5 | atom.config.set('autocomplete-plus.enableAutoActivation', true) 6 | completionDelay = 100 7 | atom.config.set('autocomplete-plus.autoActivationDelay', completionDelay) 8 | completionDelay += 100 // Rendering delay 9 | 10 | const workspaceElement = atom.views.getView(atom.workspace) 11 | jasmine.attachToDOM(workspaceElement) 12 | 13 | let autocompleteSnippetsMainModule = null 14 | let snippetsMainModule = null 15 | const autocompleteManager = null 16 | 17 | waitsForPromise(() => 18 | Promise.all([ 19 | atom.workspace.open('sample.js').then((e) => { 20 | editor = e 21 | editorView = atom.views.getView(editor) 22 | }), 23 | 24 | atom.packages.activatePackage('language-javascript'), 25 | atom.packages.activatePackage('autocomplete-snippets').then(({mainModule}) => autocompleteSnippetsMainModule = mainModule), 26 | 27 | atom.packages.activatePackage('autocomplete-plus'), 28 | atom.packages.activatePackage('snippets').then(({mainModule}) => { 29 | snippetsMainModule = mainModule 30 | snippetsMainModule.loaded = false 31 | }) 32 | ]) 33 | ) 34 | 35 | waitsFor('snippets provider to be registered', 1000, () => autocompleteSnippetsMainModule.provider != null) 36 | 37 | waitsFor('all snippets to load', 3000, () => snippetsMainModule.loaded) 38 | }) 39 | 40 | describe('when autocomplete-plus is enabled', () => { 41 | it('shows autocompletions when there are snippets available', () => { 42 | runs(() => { 43 | expect(editorView.querySelector('.autocomplete-plus')).not.toExist() 44 | 45 | editor.moveToBottom() 46 | editor.insertText('D') 47 | editor.insertText('o') 48 | 49 | advanceClock(completionDelay) 50 | }) 51 | 52 | waitsFor('autocomplete view to appear', 1000, () => editorView.querySelector('.autocomplete-plus span.word')) 53 | 54 | runs(() => { 55 | expect(editorView.querySelector('.autocomplete-plus span.word')).toHaveText('do') 56 | expect(editorView.querySelector('.autocomplete-plus span.right-label')).toHaveText('do') 57 | }) 58 | }) 59 | 60 | it("expands the snippet on confirm", () => { 61 | runs(() => { 62 | expect(editorView.querySelector('.autocomplete-plus')).not.toExist() 63 | 64 | editor.moveToBottom() 65 | editor.insertText('D') 66 | editor.insertText('o') 67 | 68 | advanceClock(completionDelay) 69 | }) 70 | 71 | waitsFor('autocomplete view to appear', 1000, () => editorView.querySelector('.autocomplete-plus span.word')) 72 | 73 | runs(() => { 74 | atom.commands.dispatch(editorView, 'autocomplete-plus:confirm') 75 | expect(editor.getText()).toContain('} while (true)') 76 | }) 77 | }) 78 | }) 79 | 80 | describe('when showing suggestions', () => 81 | it('sorts them in alphabetical order', () => { 82 | const unorderedPrefixes = [ 83 | "", 84 | "dop", 85 | "do", 86 | "dad", 87 | "d" 88 | ] 89 | 90 | const snippets = {} 91 | for (let x of Array.from(unorderedPrefixes)) { 92 | snippets[x] = {prefix: x, name: "", description: "", descriptionMoreURL: ""} 93 | } 94 | 95 | const SnippetsProvider = require('../lib/snippets-provider') 96 | const sp = new SnippetsProvider() 97 | sp.setSnippetsSource({snippetsForScopes(scope) { 98 | return snippets 99 | }}) 100 | const suggestions = sp.getSuggestions({scopeDescriptor: "", prefix: "d"}) 101 | 102 | const suggestionsText = suggestions.map(x => x.text) 103 | expect(suggestionsText).toEqual(["d", "dad", "do", "dop"]) 104 | }) 105 | ) 106 | }) 107 | -------------------------------------------------------------------------------- /spec/fixtures/sample.js: -------------------------------------------------------------------------------- 1 | console.log("ohai"); 2 | --------------------------------------------------------------------------------