diff --git a/src/vs/editor/contrib/suggest/common/suggest.ts b/src/vs/editor/contrib/suggest/common/suggest.ts index b6f55b74dc3389d9e3df0a64340d54c31d420a9b..03dd51c817efa5f0f71761bf69930612e4891e8f 100644 --- a/src/vs/editor/contrib/suggest/common/suggest.ts +++ b/src/vs/editor/contrib/suggest/common/suggest.ts @@ -13,9 +13,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IModel, IPosition } from 'vs/editor/common/editorCommon'; import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry } from 'vs/editor/common/modes'; -import { ISnippetsRegistry, Extensions } from 'vs/editor/common/modes/snippetsRegistry'; import { Position } from 'vs/editor/common/core/position'; -import { Registry } from 'vs/platform/platform'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { DefaultConfig } from 'vs/editor/common/config/defaultConfig'; @@ -36,20 +34,13 @@ export interface ISuggestionItem { export type SnippetConfig = 'top' | 'bottom' | 'inline' | 'none'; +let _snippetSuggestSupport: ISuggestSupport; -// add suggestions from snippet registry. -export const snippetSuggestSupport: ISuggestSupport = { - - triggerCharacters: [], - - provideCompletionItems(model: IModel, position: Position): ISuggestResult { - const suggestions = Registry.as(Extensions.Snippets).getSnippetCompletions(model, position); - if (suggestions) { - return { suggestions }; - } - return undefined; - } -}; +export function setSnippetSuggestSupport(support: ISuggestSupport): ISuggestSupport { + const old = _snippetSuggestSupport; + _snippetSuggestSupport = support; + return old; +} export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[]): TPromise { @@ -62,8 +53,8 @@ export function provideSuggestionItems(model: IModel, position: Position, snippe const supports = SuggestRegistry.orderedGroups(model); // add snippets provider unless turned off - if (snippetConfig !== 'none') { - supports.unshift([snippetSuggestSupport]); + if (snippetConfig !== 'none' && _snippetSuggestSupport) { + supports.unshift([_snippetSuggestSupport]); } // add suggestions from contributed providers - providers are ordered in groups of @@ -103,7 +94,7 @@ export function provideSuggestionItems(model: IModel, position: Position, snippe } } - if (len !== allSuggestions.length && support !== snippetSuggestSupport) { + if (len !== allSuggestions.length && support !== _snippetSuggestSupport) { hasResult = true; } @@ -217,7 +208,7 @@ CommonEditorRegistry.registerDefaultLanguageCommand('_executeCompletionItemProvi return provideSuggestionItems(model, position).then(items => { - for (const {container, suggestion} of items) { + for (const { container, suggestion } of items) { result.incomplete = result.incomplete || container.incomplete; result.suggestions.push(suggestion); } diff --git a/src/vs/workbench/api/node/extHost.contribution.ts b/src/vs/workbench/api/node/extHost.contribution.ts index 615614dd8e7864143cbd3d35a8a15f2aec119915..2f4695163cf3668cb58885d5a2d19b594bbfa912 100644 --- a/src/vs/workbench/api/node/extHost.contribution.ts +++ b/src/vs/workbench/api/node/extHost.contribution.ts @@ -36,7 +36,6 @@ import { MainThreadFileSystemEventService } from './mainThreadFileSystemEventSer import { MainThreadSCM } from './mainThreadSCM'; // --- other interested parties -import { MainProcessTextMateSnippet } from 'vs/editor/node/textMate/TMSnippets'; import { JSONValidationExtensionPoint } from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint'; import { LanguageConfigurationFileHandler } from 'vs/editor/node/languageConfigurationExtensionPoint'; import { SaveParticipant } from './mainThreadSaveParticipant'; @@ -90,7 +89,6 @@ export class ExtHostContribution implements IWorkbenchContribution { col.finish(true, this.threadService); // Other interested parties - create(MainProcessTextMateSnippet); create(JSONValidationExtensionPoint); this.instantiationService.createInstance(LanguageConfigurationFileHandler); create(MainThreadFileSystemEventService); diff --git a/src/vs/editor/node/textMate/TMSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts similarity index 85% rename from src/vs/editor/node/textMate/TMSnippets.ts rename to src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts index 3902495c507a55996ca3f8b01a54a5d159cb65d1..3440689b48f621f18fc0ccf8dacb004b3f6b5b96 100644 --- a/src/vs/editor/node/textMate/TMSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts @@ -5,18 +5,18 @@ 'use strict'; import * as nls from 'vs/nls'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; 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 { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; -import { ISnippetsRegistry, Extensions, ISnippet } from 'vs/editor/common/modes/snippetsRegistry'; +import { ISnippetsService, ISnippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; import { IModeService } from 'vs/editor/common/services/modeService'; -import platform = require('vs/platform/platform'); import { languagesExtPoint } from 'vs/editor/common/services/modeServiceImpl'; import { LanguageIdentifier } from 'vs/editor/common/modes'; -export interface ISnippetsExtensionPoint { +interface ISnippetsExtensionPoint { language: string; path: string; } @@ -41,12 +41,12 @@ let snippetsExtensionPoint = ExtensionsRegistry.registerExtensionPoint { for (let i = 0; i < extensions.length; i++) { let tmSnippets = extensions[i].value; @@ -57,6 +57,10 @@ export class MainProcessTextMateSnippet { }); } + getId() { + return 'tmSnippetExtension'; + } + private _withSnippetContribution(extensionName: string, extensionFolderPath: string, snippet: ISnippetsExtensionPoint, collector: ExtensionMessageCollector): 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))); @@ -79,19 +83,17 @@ export class MainProcessTextMateSnippet { if (mode.getId() !== modeId) { return; } - readAndRegisterSnippets(languageIdentifier, normalizedAbsolutePath, extensionName); + readAndRegisterSnippets(this._snippetService, languageIdentifier, normalizedAbsolutePath, extensionName); disposable.dispose(); }); } } } -let snippetsRegistry = platform.Registry.as(Extensions.Snippets); - -export function readAndRegisterSnippets(languageIdentifier: LanguageIdentifier, filePath: string, ownerName: string): TPromise { +export function readAndRegisterSnippets(snippetService: ISnippetsService, languageIdentifier: LanguageIdentifier, filePath: string, ownerName: string): TPromise { return readFile(filePath).then(fileContents => { let snippets = parseSnippetFile(fileContents.toString(), ownerName); - snippetsRegistry.registerSnippets(languageIdentifier, snippets, filePath); + snippetService.registerSnippets(languageIdentifier, snippets, filePath); }); } diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts index 17b5c6962366eb8fd3eb8b07f16864ea2ec6d728..18734256db22f4ef8674097ddac85acd5856346b 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -5,16 +5,15 @@ 'use strict'; import * as nls from 'vs/nls'; -import { Registry } from 'vs/platform/platform'; import { TPromise } from 'vs/base/common/winjs.base'; import { ICommonCodeEditor, EditorContextKeys } from 'vs/editor/common/editorCommon'; import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; import { SnippetController } from 'vs/editor/contrib/snippet/common/snippetController'; import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; -import { ISnippetsRegistry, Extensions, ISnippet } from 'vs/editor/common/modes/snippetsRegistry'; import { IModeService } from 'vs/editor/common/services/modeService'; import { LanguageId } from 'vs/editor/common/modes'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ISnippetsService, ISnippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; interface ISnippetPick extends IPickOpenEntry { snippet: ISnippet; @@ -26,7 +25,7 @@ class Args { if (!arg || typeof arg !== 'object') { return Args._empty; } - let {snippet, name, langId} = arg; + let { snippet, name, langId } = arg; if (typeof snippet !== 'string') { snippet = undefined; } @@ -65,14 +64,15 @@ class InsertSnippetAction extends EditorAction { public run(accessor: ServicesAccessor, editor: ICommonCodeEditor, arg: any): TPromise { const modeService = accessor.get(IModeService); + const snippetService = accessor.get(ISnippetsService); if (!editor.getModel()) { return undefined; } const quickOpenService = accessor.get(IQuickOpenService); - const {lineNumber, column} = editor.getPosition(); - let {snippet, name, langId} = Args.fromUser(arg); + const { lineNumber, column } = editor.getPosition(); + let { snippet, name, langId } = Args.fromUser(arg); return new TPromise((resolve, reject) => { @@ -96,7 +96,7 @@ class InsertSnippetAction extends EditorAction { // validate the `languageId` to ensure this is a user // facing language with a name and the chance to have // snippets, else fall back to the outer language - const {language} = modeService.getLanguageIdentifier(languageId); + const { language } = modeService.getLanguageIdentifier(languageId); if (!modeService.getLanguageName(language)) { languageId = editor.getModel().getLanguageIdentifier().id; } @@ -104,7 +104,7 @@ class InsertSnippetAction extends EditorAction { if (name) { // take selected snippet - Registry.as(Extensions.Snippets).visitSnippets(languageId, snippet => { + snippetService.visitSnippets(languageId, snippet => { if (snippet.name !== name) { return true; } @@ -114,7 +114,7 @@ class InsertSnippetAction extends EditorAction { } else { // let user pick a snippet const picks: ISnippetPick[] = []; - Registry.as(Extensions.Snippets).visitSnippets(languageId, snippet => { + snippetService.visitSnippets(languageId, snippet => { picks.push({ label: snippet.prefix, detail: snippet.description, diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts index 3ae8d103b34f8622edbf99e727ae42c9f5295ca8..548f138ad7881b701a580915b792f3bc7fde6703 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import 'vs/workbench/parts/snippets/electron-browser/snippetsService'; import 'vs/workbench/parts/snippets/electron-browser/insertSnippet'; import 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; @@ -16,6 +17,7 @@ import platform = require('vs/platform/platform'); import workbenchActionRegistry = require('vs/workbench/common/actionRegistry'); import workbenchContributions = require('vs/workbench/common/contributions'); import snippetsTracker = require('./snippetsTracker'); +import tmSnippets = require('./TMSnippets'); import * as pfs from 'vs/base/node/pfs'; import errors = require('vs/base/common/errors'); import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; @@ -121,6 +123,9 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenSn (platform.Registry.as(workbenchContributions.Extensions.Workbench)).registerWorkbenchContribution( snippetsTracker.SnippetsTracker ); +(platform.Registry.as(workbenchContributions.Extensions.Workbench)).registerWorkbenchContribution( + tmSnippets.MainProcessTextMateSnippet +); let schemaId = 'vscode://schemas/snippets'; let schema: IJSONSchema = { diff --git a/src/vs/editor/common/modes/snippetsRegistry.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts similarity index 81% rename from src/vs/editor/common/modes/snippetsRegistry.ts rename to src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index 6803d097fe8fc8872cb6fcca3712e7ab183dc767..1c41fbfcf1e2389f624a5d0a312b3d80a06c2212 100644 --- a/src/vs/editor/common/modes/snippetsRegistry.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -6,31 +6,21 @@ import { localize } from 'vs/nls'; import * as strings from 'vs/base/common/strings'; -import { ITokenizedModel, IPosition } from 'vs/editor/common/editorCommon'; +import { IModel, IPosition } from 'vs/editor/common/editorCommon'; import { ISuggestion, LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; -import { Registry } from 'vs/platform/platform'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { setSnippetSuggestSupport } from 'vs/editor/contrib/suggest/common/suggest'; -export const Extensions = { - Snippets: 'base.contributions.snippets' -}; +export const ISnippetsService = createDecorator('snippetService'); -export interface ISnippetsRegistry { +export interface ISnippetsService { + + _serviceBrand: any; - /** - * Register a snippet to the registry. - */ registerSnippets(languageIdentifier: LanguageIdentifier, snippets: ISnippet[], owner?: string): void; - /** - * Visit all snippets - */ visitSnippets(languageId: LanguageId, accept: (snippet: ISnippet) => void): void; - - /** - * Get all snippet completions for the given position - */ - getSnippetCompletions(model: ITokenizedModel, position: IPosition): ISuggestion[]; - } export interface ISnippet { @@ -45,10 +35,22 @@ interface ISnippetSuggestion extends ISuggestion { disambiguateLabel: string; } -class SnippetsRegistry implements ISnippetsRegistry { +class SnippetsService implements ISnippetsService { + + _serviceBrand: any; private _snippets: { [owner: string]: ISnippet[] }[] = []; + constructor() { + setSnippetSuggestSupport({ + triggerCharacters: undefined, + provideCompletionItems: (model, position) => { + const suggestions = this.getSnippetCompletions(model, position); + return { suggestions }; + } + }); + } + public registerSnippets(languageIdentifier: LanguageIdentifier, snippets: ISnippet[], owner = ''): void { let snippetsByMode = this._snippets[languageIdentifier.id]; if (!snippetsByMode) { @@ -69,7 +71,7 @@ class SnippetsRegistry implements ISnippetsRegistry { } } - public getSnippetCompletions(model: ITokenizedModel, position: IPosition): ISuggestion[] { + public getSnippetCompletions(model: IModel, position: IPosition): ISuggestion[] { const languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column); if (!this._snippets[languageId]) { return undefined; @@ -116,7 +118,7 @@ class SnippetsRegistry implements ISnippetsRegistry { // dismbiguate suggestions with same labels let lastSuggestion: ISnippetSuggestion; - for (const suggestion of result.sort(SnippetsRegistry._compareSuggestionsByLabel)) { + for (const suggestion of result.sort(SnippetsService._compareSuggestionsByLabel)) { if (lastSuggestion && lastSuggestion.label === suggestion.label) { // use the disambiguateLabel instead of the actual label lastSuggestion.label = lastSuggestion.disambiguateLabel; @@ -133,6 +135,8 @@ class SnippetsRegistry implements ISnippetsRegistry { } } +registerSingleton(ISnippetsService, SnippetsService); + export interface ISimpleModel { getLineContent(lineNumber): string; } @@ -161,6 +165,3 @@ export function getNonWhitespacePrefix(model: ISimpleModel, position: IPosition) return ''; } -const snippetsRegistry: ISnippetsRegistry = new SnippetsRegistry(); -Registry.add(Extensions.Snippets, snippetsRegistry); - diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts index 3a3eca2d16f30cf4cf3a157bba1a66b046d92088..0b3c2696bdcdfe59ccf74b0e23cf69b143de79f6 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts @@ -13,7 +13,8 @@ import winjs = require('vs/base/common/winjs.base'); import { mkdirp, fileExists, readdir } from 'vs/base/node/pfs'; import { onUnexpectedError } from 'vs/base/common/errors'; import lifecycle = require('vs/base/common/lifecycle'); -import { readAndRegisterSnippets } from 'vs/editor/node/textMate/TMSnippets'; +import { readAndRegisterSnippets } from './TMSnippets'; +import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; @@ -31,6 +32,7 @@ export class SnippetsTracker implements workbenchExt.IWorkbenchContribution { constructor( @ILifecycleService private lifecycleService: ILifecycleService, @IModeService private modeService: IModeService, + @ISnippetsService private snippetService: ISnippetsService, @IEnvironmentService environmentService: IEnvironmentService, @IExtensionService extensionService: IExtensionService ) { @@ -75,7 +77,7 @@ export class SnippetsTracker implements workbenchExt.IWorkbenchContribution { var snippetPath = paths.join(this.snippetFolder, snippetFile); let languageIdentifier = this.modeService.getLanguageIdentifier(modeId); if (languageIdentifier) { - return readAndRegisterSnippets(languageIdentifier, snippetPath, localize('userSnippet', "User Snippet")); + return readAndRegisterSnippets(this.snippetService, languageIdentifier, snippetPath, localize('userSnippet', "User Snippet")); } return undefined; })); diff --git a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts index 8f9a36901ae23445635361db89115f6a8bb4e2ae..4187fe4845f490e22a813d2919e3919e3c10578c 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { KeyCode } from 'vs/base/common/keyCodes'; import { RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { ISnippetsRegistry, Extensions, getNonWhitespacePrefix, ISnippet } from 'vs/editor/common/modes/snippetsRegistry'; +import { ISnippetsService, getNonWhitespacePrefix, ISnippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; import { Registry } from 'vs/platform/platform'; import { endsWith } from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -20,8 +20,6 @@ import { IConfigurationRegistry, Extensions as ConfigExt } from "vs/platform/con import EditorContextKeys = editorCommon.EditorContextKeys; -let snippetsRegistry = Registry.as(Extensions.Snippets); - @commonEditorContribution export class TabCompletionController implements editorCommon.IEditorContribution { @@ -38,7 +36,8 @@ export class TabCompletionController implements editorCommon.IEditorContribution constructor( editor: editorCommon.ICommonCodeEditor, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @ISnippetsService snippetService: ISnippetsService ) { this._snippetController = SnippetController.get(editor); const hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService); @@ -59,7 +58,7 @@ export class TabCompletionController implements editorCommon.IEditorContribution } if (selectFn) { - snippetsRegistry.visitSnippets(editor.getModel().getLanguageIdentifier().id, s => { + snippetService.visitSnippets(editor.getModel().getLanguageIdentifier().id, s => { if (selectFn(s)) { this._currentSnippets.push(s); } diff --git a/src/vs/editor/test/common/modes/snippetsRegistry.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts similarity index 97% rename from src/vs/editor/test/common/modes/snippetsRegistry.test.ts rename to src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts index 27b69ba380b96a465aafd63503a060d5f0d7f2fb..6c67fdb6accb70fea6a86486a6f827f0f7216c12 100644 --- a/src/vs/editor/test/common/modes/snippetsRegistry.test.ts +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts @@ -6,7 +6,8 @@ 'use strict'; import * as assert from 'assert'; -import { getNonWhitespacePrefix } from 'vs/editor/common/modes/snippetsRegistry'; +import { getNonWhitespacePrefix } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; + suite('getNonWhitespacePrefix', () => {