From 20b534d41eec11ab39015b0b2d6d2f0ea46cfbb5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Nov 2020 15:45:02 +0100 Subject: [PATCH] add persisted snippet enablement --- .../snippets/browser/snippets.contribution.ts | 8 ++- .../contrib/snippets/browser/snippetsFile.ts | 5 +- .../snippets/browser/snippetsService.ts | 67 +++++++++++++++++-- .../test/browser/snippetsService.test.ts | 9 ++- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts index c7adf5c4fcc..a98a176e686 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts @@ -19,9 +19,13 @@ export interface ISnippetsService { getSnippetFiles(): Promise>; - getSnippets(languageId: LanguageId): Promise; + isEnabled(snippet: Snippet): boolean; - getSnippetsSync(languageId: LanguageId): Snippet[]; + updateEnablement(snippet: Snippet, enabled: string): void; + + getSnippets(languageId: LanguageId, includeDisabledSnippets?: boolean): Promise; + + getSnippetsSync(languageId: LanguageId, includeDisabledSnippets?: boolean): Snippet[]; } const languageScopeSchemaId = 'vscode://schemas/snippets'; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts index a9e0f87a05b..f4b312fc7c0 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts @@ -15,6 +15,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IdleValue } from 'vs/base/common/async'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { relativePath } from 'vs/base/common/resources'; class SnippetBodyInsights { @@ -86,6 +87,7 @@ export class Snippet { readonly body: string, readonly source: string, readonly snippetSource: SnippetSource, + readonly snippetIdentifier?: string ) { // this.prefixLow = prefix ? prefix.toLowerCase() : prefix; @@ -289,7 +291,8 @@ export class SnippetFile { description, body, source, - this.source + this.source, + this._extension && `${relativePath(this._extension.extensionLocation, this.location)}/${name}` )); }); } diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 5d1e9d1eb38..50eed66c21d 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -26,6 +26,10 @@ import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchMo import { SnippetCompletionProvider } from './snippetCompletionProvider'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { ResourceMap } from 'vs/base/common/map'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { isStringArray } from 'vs/base/common/types'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; namespace snippetExt { @@ -124,6 +128,47 @@ function watch(service: IFileService, resource: URI, callback: () => any): IDisp ); } +class SnippetEnablement { + + private static _key = 'snippets.ignoredSnippets'; + + private readonly _ignored: Set; + + constructor( + @IStorageService private readonly _storageService: IStorageService, + @IStorageKeysSyncRegistryService storageKeysSyncService: IStorageKeysSyncRegistryService, + ) { + + storageKeysSyncService.registerStorageKey({ key: SnippetEnablement._key, version: 1 }); + + const raw = _storageService.get(SnippetEnablement._key, StorageScope.GLOBAL, ''); + let data: string[] | undefined; + try { + data = JSON.parse(raw); + } catch { } + + this._ignored = isStringArray(data) ? new Set(data) : new Set(); + } + + isIgnored(id: string): boolean { + return this._ignored.has(id); + } + + updateIgnored(id: string, value: boolean): void { + let changed = false; + if (this._ignored.has(id) && !value) { + this._ignored.delete(id); + changed = true; + } else if (!this._ignored.has(id) && value) { + this._ignored.add(id); + changed = true; + } + if (changed) { + this._storageService.store(SnippetEnablement._key, JSON.stringify(Array.from(this._ignored)), StorageScope.GLOBAL); + } + } +} + class SnippetsService implements ISnippetsService { declare readonly _serviceBrand: undefined; @@ -131,6 +176,7 @@ class SnippetsService implements ISnippetsService { private readonly _disposables = new DisposableStore(); private readonly _pendingWork: Promise[] = []; private readonly _files = new ResourceMap(); + private readonly _enablement: SnippetEnablement; constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, @@ -140,6 +186,7 @@ class SnippetsService implements ISnippetsService { @IFileService private readonly _fileService: IFileService, @IExtensionResourceLoaderService private readonly _extensionResourceLoaderService: IExtensionResourceLoaderService, @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService instantiationService: IInstantiationService, ) { this._pendingWork.push(Promise.resolve(lifecycleService.when(LifecyclePhase.Restored).then(() => { this._initExtensionSnippets(); @@ -148,12 +195,24 @@ class SnippetsService implements ISnippetsService { }))); setSnippetSuggestSupport(new SnippetCompletionProvider(this._modeService, this)); + + this._enablement = instantiationService.createInstance(SnippetEnablement); } dispose(): void { this._disposables.dispose(); } + isEnabled(snippet: Snippet): boolean { + return !snippet.snippetIdentifier || !this._enablement.isIgnored(snippet.snippetIdentifier); + } + + updateEnablement(snippet: Snippet, enabled: string): void { + if (snippet.snippetIdentifier) { + this._enablement.updateIgnored(snippet.snippetIdentifier, !enabled); + } + } + private _joinSnippets(): Promise { const promises = this._pendingWork.slice(0); this._pendingWork.length = 0; @@ -165,7 +224,7 @@ class SnippetsService implements ISnippetsService { return this._files.values(); } - async getSnippets(languageId: LanguageId): Promise { + async getSnippets(languageId: LanguageId, includeIgnored?: boolean): Promise { await this._joinSnippets(); const result: Snippet[] = []; @@ -182,10 +241,10 @@ class SnippetsService implements ISnippetsService { } } await Promise.all(promises); - return result; + return includeIgnored ? result : result.filter(this.isEnabled, this); } - getSnippetsSync(languageId: LanguageId): Snippet[] { + getSnippetsSync(languageId: LanguageId, includeIgnored?: boolean): Snippet[] { const result: Snippet[] = []; const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); if (languageIdentifier) { @@ -197,7 +256,7 @@ class SnippetsService implements ISnippetsService { file.select(langName, result); } } - return result; + return includeIgnored ? result : result.filter(this.isEnabled, this); } // --- loading, watching diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index cb3b8cbc6c5..10419ba6cdd 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -16,8 +16,7 @@ import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes class SimpleSnippetService implements ISnippetsService { declare readonly _serviceBrand: undefined; - constructor(readonly snippets: Snippet[]) { - } + constructor(readonly snippets: Snippet[]) { } getSnippets() { return Promise.resolve(this.getSnippetsSync()); } @@ -27,6 +26,12 @@ class SimpleSnippetService implements ISnippetsService { getSnippetFiles(): any { throw new Error(); } + isEnabled(): boolean { + throw new Error(); + } + updateEnablement(): void { + throw new Error(); + } } suite('SnippetsService', function () {