Skip to content

Commit

Permalink
Merge pull request #1381 from vuejs/scaffold-snippet
Browse files Browse the repository at this point in the history
Scaffold snippet. Fix #1151
  • Loading branch information
octref authored Aug 9, 2019
2 parents 16e6f80 + 47b27d0 commit 7589443
Show file tree
Hide file tree
Showing 26 changed files with 372 additions and 222 deletions.
5 changes: 3 additions & 2 deletions client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { resolve } from 'path';
import { existsSync } from 'fs';

export function initializeLanguageClient(vlsModulePath: string): LanguageClient {
export function initializeLanguageClient(vlsModulePath: string, globalSnippetDir: string): LanguageClient {
const debugOptions = { execArgv: ['--nolazy', '--inspect=6005'] };

const documentSelector = ['vue'];
Expand Down Expand Up @@ -43,7 +43,8 @@ export function initializeLanguageClient(vlsModulePath: string): LanguageClient
fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.js,**/*.ts}', false, false, true)
},
initializationOptions: {
config
config,
globalSnippetDir
},
revealOutputChannelOn: RevealOutputChannelOn.Never
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import { writeFileSync } from 'fs';
import { resolve } from 'path';
import { getGeneratedGrammar } from './grammar';
import { getGeneratedGrammar } from '../grammar';

export function generateGrammarCommandHandler(extensionPath: string) {
return () => {
Expand Down
8 changes: 8 additions & 0 deletions client/commands/openUserScaffoldSnippetFolderCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as vscode from 'vscode';

export function generateOpenUserScaffoldSnippetFolderCommand(globalSnippetDir: string) {
return async () => {
const uri = vscode.Uri.file(globalSnippetDir);
vscode.commands.executeCommand('vscode.openFolder', uri, true);
};
}
File renamed without changes.
15 changes: 15 additions & 0 deletions client/userSnippetDir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import { homedir } from 'os';
import { resolve } from 'path';

export function getGlobalSnippetDir(isInsiders: boolean) {
const appName = isInsiders ? 'Code - Insiders' : 'Code';

if (process.platform === 'win32') {
return resolve(process.env['APPDATA'] || '', appName, 'User/snippets/vetur');
} else if (process.platform === 'darwin') {
return resolve(homedir(), 'Library/Application Support', appName, 'User/snippets/vetur');
} else {
return resolve(homedir(), '.config', appName, 'User/snippets/vetur');
}
}
21 changes: 18 additions & 3 deletions client/vueMain.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import * as vscode from 'vscode';
import { LanguageClient, WorkspaceEdit } from 'vscode-languageclient';
import { generateGrammarCommandHandler } from './generate_grammar';
import { generateGrammarCommandHandler } from './commands/generateGrammarCommand';
import { registerLanguageConfigurations } from './languages';
import { initializeLanguageClient } from './client';
import { join } from 'path';
import {
setVirtualContents,
registerVeturTextDocumentProviders,
generateShowVirtualFileCommand
} from './virtualFileCommands';
} from './commands/virtualFileCommand';
import { getGlobalSnippetDir } from './userSnippetDir';
import { generateOpenUserScaffoldSnippetFolderCommand } from './commands/openUserScaffoldSnippetFolderCommand';

export async function activate(context: vscode.ExtensionContext) {
const isInsiders = vscode.env.appName.includes('Insiders');
const globalSnippetDir = getGlobalSnippetDir(isInsiders);

/**
* Virtual file display command for debugging template interpolation
*/
Expand All @@ -23,6 +28,16 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('vetur.generateGrammar', generateGrammarCommandHandler(context.extensionPath))
);

/**
* Open custom snippet folder
*/
context.subscriptions.push(
vscode.commands.registerCommand(
'vetur.openUserScaffoldSnippetFolder',
generateOpenUserScaffoldSnippetFolderCommand(globalSnippetDir)
)
);

context.subscriptions.push(
vscode.commands.registerCommand('vetur.applyWorkspaceEdits', (args: WorkspaceEdit) => {
const edit = client.protocol2CodeConverter.asWorkspaceEdit(args)!;
Expand All @@ -45,7 +60,7 @@ export async function activate(context: vscode.ExtensionContext) {
*/

const serverModule = context.asAbsolutePath(join('server', 'dist', 'vueServerMain.js'));
const client = initializeLanguageClient(serverModule);
const client = initializeLanguageClient(serverModule, globalSnippetDir);
context.subscriptions.push(client.start());

client
Expand Down
Binary file added docs/images/snippet-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/snippet-partial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 39 additions & 1 deletion docs/snippet.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,42 @@ export default {
<style lang="scss">
</style>
```
```

## Customizable Scaffold Snippets

Three sources supplement Vetur with scaffold snippets:

![Snippet Main](./images/snippet-main.png)

- 💼 Workspace. Located at `<WORKSPACE>/.vscode/vetur/snippets`. These scaffold snippets are only available in the workspace.
- 🗒️ User data directory. You can open the folder with the command `Vetur: Open user scaffold snippet folder`. These scaffold snippets are available in all workspaces.
- ✌ Vetur. Vetur offers a few scaffold snippets out of the box.

The workspace/user Vetur snippet folders share the same structure:

```
vetur/snippets
├── docs
│ │ // Completed as `<docs>`. Will have default completion icon.
│ └── docs.vue
├── style
│ │ // Completed as `<style>`. Will have CSS completion icon.
│ │ // `template` and `script` folder will have HTML/JS icons.
│ └── scss-module.vue
└── vue-class-component.vue // Top level files will be completed as `<vue>`
```

Completions of scaffold snippets are sorted by their categories. Workspace > User > Vetur.

You can customize the suffix and turn sources on/off with `vetur.completion.scaffoldSnippetSources`:

```json
"vetur.completion.scaffoldSnippetSources": {
"workspace": "💼", // Suffix workspace snippets with `💼`
"user": "(️User)", // Suffix workspace snippets with `(User)`
"vetur": "" // Disable Vetur's builtin scaffold snippets
}
```

![Snippet Partial](./images/snippet-main.png)
37 changes: 32 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"scripts": {
"build:grammar": "tsc -p . && node dist/scripts/build_grammar",
"compile": "tsc -b .",
"watch": "tsc -b -w .",
"watch": "run-s compile copy:snippets watch:build",
"watch:build": "tsc -b -w .",
"copy:snippets": "cp -r server/src/modes/vue/veturSnippets server/dist/modes/vue/veturSnippets",
"lint": "tslint -c tslint.json client/**/*.ts server/**/*.ts scripts/**/*.ts",
"test:server": "npm run compile && cd server && npm test",
"test:e2e": "node ./dist/test/codeTestRunner.js",
Expand Down Expand Up @@ -69,6 +71,10 @@
{
"command": "vetur.showCorrespondingVirtualFile",
"title": "Vetur: Show corresponding virtual file and sourcemap"
},
{
"command": "vetur.openUserScaffoldSnippetFolder",
"title": "Vetur: Open user scaffold snippet folder"
}
],
"breakpoints": [
Expand Down Expand Up @@ -175,10 +181,31 @@
"default": true,
"description": "Include completion for module export and auto import them"
},
"vetur.completion.useScaffoldSnippets": {
"type": "boolean",
"default": true,
"description": "Enable/disable Vetur's built-in scaffolding snippets"
"vetur.completion.scaffoldSnippetSources": {
"type": "object",
"default": {
"workspace": "💼",
"user": "🗒️",
"vetur": ""
},
"properties": {
"workspace": {
"type": "string",
"default": "💼",
"description": "Show Scaffold Snippets from `<WORKSPACE>/.vscode/vetur/snippets`."
},
"user": {
"type": "string",
"default": "🗒️",
"description": "Show Scaffold Snippets from `<USER-DATA-DIR>/User/snippets/vetur`."
},
"vetur": {
"type": "string",
"default": "",
"description": "Show Scaffold Snippets bundled in Vetur."
}
},
"description": "Where Vetur source Scaffold Snippets from and how to indicate them. Set a source to \"\" to disable it.\n\n- workspace: `<WORKSPACE>/.vscode/vetur/snippets`.\n- user: `<USER-DATA-DIR>/User/snippets/vetur`.\n- vetur: Bundled in Vetur.\n\nThe default is:\n```\n\"vetur.completion.scaffoldSnippetSources\": {\n \"workspace\": \"💼\",\n \"user\": \"🗒️\",\n \"vetur\": \"\"\n}\n```\n\nAlternatively, you can do:\n\n```\n\"vetur.completion.scaffoldSnippetSources\": {\n \"workspace\": \"(W)\",\n \"user\": \"(U)\",\n \"vetur\": \"(V)\"\n}\n```\n\nRead more: https://vuejs.github.io/vetur/snippet.html."
},
"vetur.completion.tagCasing": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions server/src/embeddedSupport/languageModes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class LanguageModes {
this.modelCaches.push(this.documentRegions);
}

async init(workspacePath: string, services: VLSServices) {
async init(workspacePath: string, services: VLSServices, globalSnippetDir: string) {
let tsModule = await import('typescript');
if (services.dependencyService) {
const ts = services.dependencyService.getDependency('typescript');
Expand Down Expand Up @@ -135,7 +135,7 @@ export class LanguageModes {
services.dependencyService
);

this.modes['vue'] = getVueMode();
this.modes['vue'] = getVueMode(workspacePath, globalSnippetDir);
this.modes['vue-html'] = vueHtmlMode;
this.modes['css'] = getCSSMode(this.documentRegions);
this.modes['postcss'] = getPostCSSMode(this.documentRegions);
Expand Down
56 changes: 45 additions & 11 deletions server/src/modes/vue/index.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,65 @@
import { LanguageMode } from '../../embeddedSupport/languageModes';
import { doScaffoldComplete } from './scaffoldCompletion';
import { SnippetManager, ScaffoldSnippetSources } from './snippets';
import { Range } from 'vscode-css-languageservice';

export function getVueMode(): LanguageMode {
export function getVueMode(workspacePath: string, globalSnippetDir: string): LanguageMode {
let config: any = {};

const snippetManager = new SnippetManager(workspacePath, globalSnippetDir);
let scaffoldSnippetSources: ScaffoldSnippetSources = {
workspace: '💼',
user: '🗒️',
vetur: '✌'
};

return {
getId() {
return 'vue';
},
configure(c) {
config = c;
if (c.vetur.completion['scaffoldSnippetSources']) {
scaffoldSnippetSources = c.vetur.completion['scaffoldSnippetSources'];
}
},
doComplete(document, position) {
if (!config.vetur.completion.useScaffoldSnippets) {
if (
scaffoldSnippetSources['workspace'] === '' &&
scaffoldSnippetSources['user'] === '' &&
scaffoldSnippetSources['vetur'] === ''
) {
return { isIncomplete: false, items: [] };
}

const offset = document.offsetAt(position);
const text = document.getText().slice(0, offset);
const needBracket = /<\w*$/.test(text);
const ret = doScaffoldComplete();
// remove duplicate <
if (needBracket) {
ret.items.forEach(item => {
item.insertText = item.insertText!.slice(1);
const lines = document
.getText()
.slice(0, offset)
.split('\n');
const currentLine = lines[position.line];

const items = snippetManager.completeSnippets(scaffoldSnippetSources);

// If a line starts with `<`, it's probably a starting region tag that can be wholly replaced
if (currentLine.length > 0 && currentLine.startsWith('<')) {
const replacementRange = Range.create(
document.positionAt(offset - currentLine.length),
document.positionAt(offset)
);
items.forEach(i => {
if (i.insertText) {
i.textEdit = {
newText: i.insertText,
range: replacementRange
};
}
});
}
return ret;

return {
isIncomplete: false,
items
};
},
onDocumentRemoved() {},
dispose() {}
Expand Down
Loading

0 comments on commit 7589443

Please sign in to comment.