mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-30 06:56:29 -05:00
155 lines
5.9 KiB
TypeScript
155 lines
5.9 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
'use strict';
|
|
|
|
import * as nls from 'vs/nls';
|
|
import {parse} from 'vs/base/common/json';
|
|
import * as paths from 'vs/base/common/paths';
|
|
import {TPromise} from 'vs/base/common/winjs.base';
|
|
import {readFile} from 'vs/base/node/pfs';
|
|
import {IMessageCollector, PluginsRegistry} from 'vs/platform/plugins/common/pluginsRegistry';
|
|
import {ISuggestion} from 'vs/editor/common/modes';
|
|
import {SnippetsRegistry} from 'vs/editor/common/modes/supports';
|
|
import {IModeService} from 'vs/editor/common/services/modeService';
|
|
import {IModelService} from 'vs/editor/common/services/modelService';
|
|
import {CodeSnippet, ExternalSnippetType} from 'vs/editor/contrib/snippet/common/snippet';
|
|
|
|
export interface ITMSnippetsExtensionPoint {
|
|
language: string;
|
|
path: string;
|
|
}
|
|
|
|
export function snippetUpdated(modeId: string, filePath: string): TPromise<void> {
|
|
return readFile(filePath).then((fileContents) => {
|
|
var errors: string[] = [];
|
|
var snippetsObj = parse(fileContents.toString(), errors);
|
|
var adaptedSnippets = TMSnippetsAdaptor.adapt(snippetsObj);
|
|
SnippetsRegistry.registerSnippets(modeId, filePath, adaptedSnippets);
|
|
});
|
|
}
|
|
|
|
let snippetsExtensionPoint = PluginsRegistry.registerExtensionPoint<ITMSnippetsExtensionPoint[]>('snippets', {
|
|
description: nls.localize('vscode.extension.contributes.snippets', 'Contributes textmate snippets.'),
|
|
type: 'array',
|
|
defaultSnippets: [ { body: [{ language: '', path: '' }] }],
|
|
items: {
|
|
type: 'object',
|
|
defaultSnippets: [ { body: { language: '{{id}}', path: './snippets/{{id}}.json.'} }] ,
|
|
properties: {
|
|
language: {
|
|
description: nls.localize('vscode.extension.contributes.snippets-language', 'Language id for which this snippet is contributed to.'),
|
|
type: 'string'
|
|
},
|
|
path: {
|
|
description: nls.localize('vscode.extension.contributes.snippets-path', 'Path of the snippets file. The path is relative to the extension folder and typically starts with \'./snippets/\'.'),
|
|
type: 'string'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
export class MainProcessTextMateSnippet {
|
|
private _modelService: IModelService;
|
|
private _modeService: IModeService;
|
|
|
|
constructor(
|
|
@IModelService modelService: IModelService,
|
|
@IModeService modeService: IModeService
|
|
) {
|
|
this._modelService = modelService;
|
|
this._modeService = modeService;
|
|
|
|
snippetsExtensionPoint.setHandler((extensions) => {
|
|
for (let i = 0; i < extensions.length; i++) {
|
|
let tmSnippets = extensions[i].value;
|
|
for (let j = 0; j < tmSnippets.length; j++) {
|
|
this._withTMSnippetContribution(extensions[i].description.extensionFolderPath, tmSnippets[j], extensions[i].collector);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private _withTMSnippetContribution(extensionFolderPath:string, snippet:ITMSnippetsExtensionPoint, collector:IMessageCollector): void {
|
|
if (!snippet.language || (typeof snippet.language !== 'string') || !this._modeService.isRegisteredMode(snippet.language)) {
|
|
collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", snippetsExtensionPoint.name, String(snippet.language)));
|
|
return;
|
|
}
|
|
if (!snippet.path || (typeof snippet.path !== 'string')) {
|
|
collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", snippetsExtensionPoint.name, String(snippet.path)));
|
|
return;
|
|
}
|
|
let normalizedAbsolutePath = paths.normalize(paths.join(extensionFolderPath, snippet.path));
|
|
|
|
if (normalizedAbsolutePath.indexOf(extensionFolderPath) !== 0) {
|
|
collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", snippetsExtensionPoint.name, normalizedAbsolutePath, extensionFolderPath));
|
|
}
|
|
|
|
let modeId = snippet.language;
|
|
let disposable = this._modeService.onDidCreateMode((mode) => {
|
|
if (mode.getId() !== modeId) {
|
|
return;
|
|
}
|
|
this.registerDefinition(modeId, normalizedAbsolutePath);
|
|
disposable.dispose();
|
|
});
|
|
}
|
|
|
|
public registerDefinition(modeId: string, filePath: string): void {
|
|
readFile(filePath).then((fileContents) => {
|
|
var errors: string[] = [];
|
|
var snippetsObj = parse(fileContents.toString(), errors);
|
|
var adaptedSnippets = TMSnippetsAdaptor.adapt(snippetsObj);
|
|
SnippetsRegistry.registerDefaultSnippets(modeId, adaptedSnippets);
|
|
});
|
|
}
|
|
}
|
|
|
|
class TMSnippetsAdaptor {
|
|
|
|
public static adapt(snippetsObj: any): ISuggestion[]{
|
|
var topLevelProperties = Object.keys(snippetsObj),
|
|
result: ISuggestion[] = [];
|
|
|
|
var processSnippet = (snippet: any, description: string) => {
|
|
var prefix = snippet['prefix'];
|
|
var bodyStringOrArray = snippet['body'];
|
|
|
|
if (Array.isArray(bodyStringOrArray)) {
|
|
bodyStringOrArray = bodyStringOrArray.join('\n');
|
|
}
|
|
|
|
if (typeof prefix === 'string' && typeof bodyStringOrArray === 'string') {
|
|
var convertedSnippet = TMSnippetsAdaptor.convertSnippet(bodyStringOrArray);
|
|
if (convertedSnippet !== null) {
|
|
result.push({
|
|
type: 'snippet',
|
|
label: prefix,
|
|
documentationLabel: snippet['description'] || description,
|
|
codeSnippet: convertedSnippet,
|
|
noAutoAccept: true
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
topLevelProperties.forEach(topLevelProperty => {
|
|
var scopeOrTemplate = snippetsObj[topLevelProperty];
|
|
if (scopeOrTemplate['body'] && scopeOrTemplate['prefix']) {
|
|
processSnippet(scopeOrTemplate, topLevelProperty);
|
|
} else {
|
|
var snippetNames = Object.keys(scopeOrTemplate);
|
|
snippetNames.forEach(name => {
|
|
processSnippet(scopeOrTemplate[name], name);
|
|
});
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
private static convertSnippet(textMateSnippet: string): string {
|
|
return CodeSnippet.convertExternalSnippet(textMateSnippet, ExternalSnippetType.TextMateSnippet);
|
|
}
|
|
}
|