diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 659be1e0fce..c256b4fa64f 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -16,6 +16,9 @@ "type": "git", "url": "https://github.com/microsoft/vscode.git" }, + "enabledApiProposals": [ + "inlineCompletionsNew" + ], "activationEvents": [ "onCommand:emmet.expandAbbreviation", "onCommand:editor.emmet.action.wrapWithAbbreviation", @@ -127,6 +130,11 @@ "default": false, "markdownDescription": "%emmetTriggerExpansionOnTab%" }, + "emmet.useInlineCompletions": { + "type": "boolean", + "default": true, + "markdownDescription": "%emmetUseInlineCompletions%" + }, "emmet.preferences": { "type": "object", "default": {}, diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 1cac53a6081..3de8f3205c1 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -59,5 +59,6 @@ "emmetPreferencesOutputInlineBreak": "The number of sibling inline elements needed for line breaks to be placed between those elements. If `0`, inline elements are always expanded onto a single line.", "emmetPreferencesOutputReverseAttributes": "If `true`, reverses attribute merging directions when resolving snippets.", "emmetPreferencesOutputSelfClosingStyle": "Style of self-closing tags: html (`
`), xml (`
`) or xhtml (`
`).", - "emmetPreferencesCssColorShort": "If `true`, color values like `#f` will be expanded to `#fff` instead of `#ffffff`." + "emmetPreferencesCssColorShort": "If `true`, color values like `#f` will be expanded to `#fff` instead of `#ffffff`.", + "emmetUseInlineCompletions": "If `true`, Emmet will use inline completions to suggest expansions." } diff --git a/extensions/emmet/src/emmetCommon.ts b/extensions/emmet/src/emmetCommon.ts index 5a5e0d872c3..82a32ac9b4e 100644 --- a/extensions/emmet/src/emmetCommon.ts +++ b/extensions/emmet/src/emmetCommon.ts @@ -23,7 +23,7 @@ import { addFileToParseCache, clearParseCache, removeFileFromParseCache } from ' export function activateEmmetExtension(context: vscode.ExtensionContext) { migrateEmmetExtensionsPath(); - registerCompletionProviders(context); + refreshCompletionProviders(context); updateEmmetExtensionsPath(); context.subscriptions.push(vscode.commands.registerCommand('editor.emmet.action.wrapWithAbbreviation', (args) => { @@ -122,8 +122,8 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { })); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('emmet.includeLanguages')) { - registerCompletionProviders(context); + if (e.affectsConfiguration('emmet.includeLanguages') || e.affectsConfiguration('emmet.useInlineCompletions')) { + refreshCompletionProviders(context); } if (e.affectsConfiguration('emmet.extensionsPath')) { updateEmmetExtensionsPath(); @@ -159,11 +159,42 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { */ const languageMappingForCompletionProviders: Map = new Map(); const completionProvidersMapping: Map = new Map(); +const completionProviderDisposables: vscode.Disposable[] = []; -function registerCompletionProviders(context: vscode.ExtensionContext) { - let completionProvider = new DefaultCompletionItemProvider(); - let includedLanguages = getMappingForIncludedLanguages(); +function refreshCompletionProviders(_: vscode.ExtensionContext) { + clearCompletionProviderInfo(); + const completionProvider = new DefaultCompletionItemProvider(); + const inlineCompletionProvider: vscode.InlineCompletionItemProviderNew = { + async provideInlineCompletionItems(document: vscode.TextDocument, position: vscode.Position, _: vscode.InlineCompletionContextNew, token: vscode.CancellationToken) { + const items = await completionProvider.provideCompletionItems(document, position, token, { triggerCharacter: undefined, triggerKind: vscode.CompletionTriggerKind.Invoke }); + if (!items) { + return undefined; + } + const item = items.items[0]; + if (!item) { + return undefined; + } + const range = item.range as vscode.Range; + + if (document.getText(range) !== item.label) { + // We only want to show an inline completion if we are really sure the user meant emmet. + // If the user types `d`, we don't want to suggest `
`. + return undefined; + } + + return [ + { + insertText: item.insertText as any, + filterText: item.label as any, + range + } + ]; + } + }; + + const useInlineCompletionProvider = vscode.workspace.getConfiguration('emmet').get('useInlineCompletions'); + const includedLanguages = getMappingForIncludedLanguages(); Object.keys(includedLanguages).forEach(language => { if (languageMappingForCompletionProviders.has(language) && languageMappingForCompletionProviders.get(language) === includedLanguages[language]) { return; @@ -178,8 +209,13 @@ function registerCompletionProviders(context: vscode.ExtensionContext) { completionProvidersMapping.delete(language); } - const provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[includedLanguages[language]]); - context.subscriptions.push(provider); + let provider; + if (useInlineCompletionProvider) { + provider = vscode.languages.registerInlineCompletionItemProviderNew({ language, scheme: '*' }, inlineCompletionProvider); + } else { + provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[includedLanguages[language]]); + } + completionProviderDisposables.push(provider); languageMappingForCompletionProviders.set(language, includedLanguages[language]); completionProvidersMapping.set(language, provider); @@ -187,8 +223,13 @@ function registerCompletionProviders(context: vscode.ExtensionContext) { Object.keys(LANGUAGE_MODES).forEach(language => { if (!languageMappingForCompletionProviders.has(language)) { - const provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[language]); - context.subscriptions.push(provider); + let provider; + if (useInlineCompletionProvider) { + provider = vscode.languages.registerInlineCompletionItemProviderNew({ language, scheme: '*' }, inlineCompletionProvider); + } else { + provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[language]); + } + completionProviderDisposables.push(provider); languageMappingForCompletionProviders.set(language, language); completionProvidersMapping.set(language, provider); @@ -196,7 +237,16 @@ function registerCompletionProviders(context: vscode.ExtensionContext) { }); } -export function deactivate() { +function clearCompletionProviderInfo() { + languageMappingForCompletionProviders.clear(); completionProvidersMapping.clear(); + let disposable: vscode.Disposable | undefined; + while (disposable = completionProviderDisposables.pop()) { + disposable.dispose(); + } +} + +export function deactivate() { + clearCompletionProviderInfo(); clearParseCache(); } diff --git a/extensions/emmet/tsconfig.json b/extensions/emmet/tsconfig.json index 994fa239537..ad07467f4ea 100644 --- a/extensions/emmet/tsconfig.json +++ b/extensions/emmet/tsconfig.json @@ -9,6 +9,7 @@ ], "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts" + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts", ] }