├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json └── settings.json ├── .vscodeignore ├── README.md ├── appveyor.yml ├── images ├── icon.png └── screenshot.png ├── package.json ├── snippet.json ├── src └── extension.ts ├── test ├── extension.test.ts ├── index.ts └── schema.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | testing 3 | node_modules 4 | *.sw? 5 | .vscode-test 6 | .DS_Store 7 | *.vsix 8 | *.log 9 | typings 10 | testing 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - "7" 6 | - "6" 7 | - "5" 8 | - "4" 9 | 10 | os: 11 | - osx 12 | - linux 13 | 14 | before_install: 15 | - if [ $TRAVIS_OS_NAME == "linux" ]; then 16 | export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; 17 | sh -e /etc/init.d/xvfb start; 18 | sleep 3; 19 | fi 20 | 21 | install: 22 | - npm install 23 | - npm run vscode:prepublish 24 | 25 | script: 26 | - npm test --silent -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceRoot}" 11 | ], 12 | "sourceMaps": true, 13 | "outFiles": [ 14 | "${workspaceRoot}/out/**/*.js" 15 | ] 16 | }, 17 | { 18 | "name": "Launch Tests", 19 | "type": "extensionHost", 20 | "request": "launch", 21 | "runtimeExecutable": "${execPath}", 22 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], 23 | "stopOnEntry": false, 24 | "sourceMaps": true, 25 | "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], 26 | // "preLaunchTask": "npm" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | out/test/** 2 | test/** -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :rotating_light: Deprecated :rotating_light: - This feature is now part of VSCode 2 | [This feature](https://github.com/Microsoft/vscode/issues/8102) now ships with visual studio code! 3 | 4 | Steps to using built-in workspace snippets: 5 | - Create a `{namehere}.code-snippets` file in `.vscode` folder 6 | - The file must be within `.vscode` folder (not in sub-folders) 7 | - Your snippets can now be checked in and shared with your team 8 | - Note: when using the snippet you will see `(Workspace Snippet)` in the intellisense autocomplete dropdown 9 | 10 | 11 | -------- 12 | 13 | # project snippets [![Build Status](https://travis-ci.org/rebornix/vscode-project-snippet.svg?branch=master)](https://travis-ci.org/rebornix/vscode-project-snippet) [![Build status](https://ci.appveyor.com/api/projects/status/0ntf4072cfp2naig/branch/master?svg=true)](https://ci.appveyor.com/project/rebornix/vscode-project-snippet/branch/master) 14 | 15 | 16 | Provide workspace/project level code snippets. 17 | 18 | ![screenshot](images/screenshot.png) 19 | 20 | ## Usage 21 | 22 | Put snippets at `.vscode/snippets/.json` 23 | 24 | For example, `.vscode/snippets/javascript.json` 25 | 26 | ```json 27 | { 28 | // Place your snippets for JavaScript here. Each snippet is defined under a snippet name and has a prefix, body and 29 | // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 30 | // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the 31 | // same ids are connected. 32 | // Example: 33 | "Print to console": { 34 | "prefix": "log", 35 | "body": [ 36 | "console.log($1);" 37 | ], 38 | "description": "Log output to console" 39 | }, 40 | "Print to console error": { 41 | "prefix": "error", 42 | "body": [ 43 | "console.error($1);" 44 | ], 45 | "description": "Log error output to console" 46 | }, 47 | } 48 | ``` 49 | 50 | See this blog post for more info. 51 | 52 | ## Contributors 53 | * rebornix 54 | * [thongdong7](https://github.com/thongdong7) 55 | 56 | ## Credits 57 |
Icons made by Maxim Basinski from www.flaticon.com is licensed by CC 3.0 BY
58 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - ps: Install-Product node 7.4.0 x64 3 | - npm install -g npm --silent 4 | 5 | build_script: 6 | - npm install 7 | - npm run vscode:prepublish 8 | 9 | test_script: 10 | - npm test --silent -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rebornix/vscode-project-snippet/fd98e479c0a6d2dd3176857cc538da0092f60cbc/images/icon.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rebornix/vscode-project-snippet/fd98e479c0a6d2dd3176857cc538da0092f60cbc/images/screenshot.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-snippets", 3 | "displayName": "Project Snippets", 4 | "description": "Project/Workspace level snippets", 5 | "version": "0.6.0", 6 | "publisher": "rebornix", 7 | "engines": { 8 | "vscode": "^1.8.0" 9 | }, 10 | "icon": "images/icon.png", 11 | "preview": true, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/rebornix/vscode-project-snippet.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/rebornix/vscode-project-snippet/issues" 18 | }, 19 | "author": { 20 | "name": "Peng Lv", 21 | "url": "https://github.com/rebornix" 22 | }, 23 | "categories": [ 24 | "Other", 25 | "Snippets" 26 | ], 27 | "activationEvents": [ 28 | "*" 29 | ], 30 | "main": "./out/src/extension", 31 | "contributes": { 32 | "jsonValidation": [ 33 | { 34 | "fileMatch": ".vscode/snippets/*.json", 35 | "url": "./snippet.json" 36 | } 37 | ] 38 | }, 39 | "scripts": { 40 | "vscode:prepublish": "tsc -p ./", 41 | "compile": "tsc -watch -p ./", 42 | "postinstall": "node ./node_modules/vscode/bin/install", 43 | "test": "node ./node_modules/vscode/bin/test" 44 | }, 45 | "dependencies": { 46 | "json5": "^0.5.1" 47 | }, 48 | "devDependencies": { 49 | "typescript": "^2.0.3", 50 | "vscode": "^1.0.0", 51 | "mocha": "^2.3.3", 52 | "@types/node": "^6.0.40", 53 | "@types/mocha": "^2.2.32" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /snippet.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema", 3 | "type": "object", 4 | "patternProperties": { 5 | ".*": { 6 | "description": "Snippet name", 7 | "additionalProperties": false, 8 | "required": [ 9 | "prefix", 10 | "body" 11 | ], 12 | "properties": { 13 | "prefix": { 14 | "type": "string", 15 | "description": "The snippet keyword" 16 | }, 17 | "body": { 18 | "type": "array", 19 | "items": { 20 | "type": "string" 21 | } 22 | }, 23 | "description": { 24 | "type": "string" 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import * as vscode from 'vscode'; 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | import * as JSON5 from 'json5'; 6 | 7 | let _snippets: { [modeId: string]: vscode.CompletionItem[] } = Object.create(null); 8 | let _registeredProviders: { [modeId: string]: vscode.Disposable } = Object.create(null); 9 | 10 | class CompletionProvider implements vscode.CompletionItemProvider { 11 | public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): 12 | vscode.CompletionItem[] | Thenable | vscode.CompletionList | Thenable { 13 | return _snippets[document.languageId]; 14 | } 15 | 16 | public resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): 17 | vscode.CompletionItem | Thenable { 18 | return item; 19 | } 20 | } 21 | 22 | export function parseSnippetFile(snippetFileContent: string): vscode.CompletionItem[] { 23 | try { 24 | if (snippetFileContent === '') { 25 | return []; 26 | } 27 | 28 | let snippetsObj = JSON5.parse(snippetFileContent); 29 | 30 | if (!snippetsObj || typeof snippetsObj !== 'object') { 31 | return []; 32 | } 33 | let topLevelProperties = Object.keys(snippetsObj); 34 | let result: vscode.CompletionItem[] = [] 35 | 36 | let processSnippet = (snippet: any, name: string) => { 37 | let prefix = snippet['prefix']; 38 | let bodyStringOrArray = snippet['body']; 39 | 40 | if (Array.isArray(bodyStringOrArray)) { 41 | bodyStringOrArray = bodyStringOrArray.join('\n'); 42 | } 43 | 44 | if (typeof prefix === 'string' && typeof bodyStringOrArray === 'string') { 45 | let newSnippet = new vscode.CompletionItem(prefix); 46 | newSnippet.kind = vscode.CompletionItemKind.Snippet; 47 | newSnippet.detail = snippet['description'] || name, 48 | newSnippet.documentation = snippet['description'] || name, 49 | newSnippet.insertText = new vscode.SnippetString(bodyStringOrArray); 50 | result.push(newSnippet); 51 | } 52 | } 53 | 54 | topLevelProperties.forEach(topLevelProperty => { 55 | let scopeOrTemplate = snippetsObj[topLevelProperty]; 56 | if (scopeOrTemplate['body'] && scopeOrTemplate['prefix']) { 57 | processSnippet(scopeOrTemplate, topLevelProperty); 58 | } else { 59 | let snippetNames = Object.keys(scopeOrTemplate); 60 | snippetNames.forEach(name => { 61 | processSnippet(scopeOrTemplate[name], name); 62 | }); 63 | } 64 | }); 65 | 66 | return result; 67 | } catch (err) { 68 | return []; 69 | } 70 | } 71 | 72 | async function readAndRegisterSnippets(filePath: vscode.Uri): Promise { 73 | return new Promise((resolve, reject) => { 74 | fs.readFile(filePath.fsPath, { encoding: 'utf8' }, (err, data) => { 75 | if (err) { 76 | reject(err); 77 | } 78 | 79 | let snippets = parseSnippetFile(data.toString()); 80 | resolve(snippets); 81 | }); 82 | }); 83 | } 84 | 85 | export async function activate(context: vscode.ExtensionContext) { 86 | let completionItemProvider = new CompletionProvider(); 87 | 88 | let snippetFiles = await vscode.workspace.findFiles(".vscode/snippets/*.json", ""); 89 | if (snippetFiles) { 90 | for (let i = 0; i < snippetFiles.length; i++) { 91 | let modeId = path.basename(snippetFiles[i].path).split('\.')[0]; 92 | let snippets = await readAndRegisterSnippets(snippetFiles[i]); 93 | _snippets[modeId] = snippets; 94 | } 95 | 96 | for (const modeId in _snippets) { 97 | let disposable = vscode.languages.registerCompletionItemProvider(modeId, completionItemProvider); 98 | _registeredProviders[modeId] = disposable; 99 | } 100 | } 101 | 102 | let snippetFilesWatcher = vscode.workspace.createFileSystemWatcher("**/.vscode/snippets/*.json"); 103 | let snippetUpdate = async (e: vscode.Uri) => { 104 | let modeId = path.basename(e.path).split('\.')[0]; 105 | let snippets = await readAndRegisterSnippets(e); 106 | _snippets[modeId] = snippets; 107 | 108 | if (_registeredProviders[modeId]) { 109 | _registeredProviders[modeId].dispose(); 110 | } 111 | 112 | let disposable = vscode.languages.registerCompletionItemProvider(modeId, completionItemProvider); 113 | _registeredProviders[modeId] = disposable; 114 | }; 115 | 116 | snippetFilesWatcher.onDidCreate(snippetUpdate); 117 | snippetFilesWatcher.onDidChange(snippetUpdate); 118 | snippetFilesWatcher.onDidDelete(async (e) => { 119 | let modeId = path.basename(e.path).split('\.')[0]; 120 | 121 | if (_registeredProviders[modeId]) { 122 | _registeredProviders[modeId].dispose(); 123 | } 124 | }); 125 | } 126 | 127 | export function deactivate() { 128 | for (let modeId in _registeredProviders) { 129 | _registeredProviders[modeId].dispose() 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /test/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | import { parseSnippetFile } from '../src/extension'; 4 | 5 | suite("parseSnippetFile", () => { 6 | test("parse content with tab indent", () => { 7 | const content = ` 8 | { 9 | \t"Print to console": { 10 | \t\t"prefix": "log", 11 | "body": [ 12 | "console.log($1);" 13 | ], 14 | "description": "Log output to console" 15 | }, 16 | } 17 | `; 18 | 19 | const actual = parseSnippetFile(content); 20 | assert.equal(1, actual.length); 21 | }); 22 | 23 | test("parse content with tab inside the string", () => { 24 | const content = ` 25 | { 26 | "Print\tto\tconsole": { 27 | "prefix": "log", 28 | "body": [ 29 | "console.log($1);" 30 | ], 31 | "description": "Log\toutput\tto\tconsole" 32 | }, 33 | } 34 | `; 35 | 36 | const actual = parseSnippetFile(content); 37 | assert.equal(1, actual.length); 38 | }); 39 | }); -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /test/schema.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | import * as JSON5 from 'json5'; 4 | import * as fs from 'fs' 5 | 6 | suite("Snippet Schema", () => { 7 | test("load snippet schema without error", () => { 8 | // This test is to ensure that there is no accidently changing in the snippet schema file which make it broken. 9 | const content = fs.readFileSync(__dirname + '/../../snippet.json'); 10 | const actual = JSON5.parse(content); 11 | 12 | assert.ok(typeof actual === 'object'); 13 | }); 14 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "." 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | ".vscode-test" 15 | ] 16 | } --------------------------------------------------------------------------------