Path: blob/main/src/vs/workbench/contrib/emmet/browser/emmetActions.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { EditorAction, ServicesAccessor, IActionOptions } from '../../../../editor/browser/editorExtensions.js';6import { grammarsExtPoint, ITMSyntaxExtensionPoint } from '../../../services/textMate/common/TMGrammars.js';7import { IExtensionService, ExtensionPointContribution } from '../../../services/extensions/common/extensions.js';8import { ICommandService } from '../../../../platform/commands/common/commands.js';9import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';1011interface ModeScopeMap {12[key: string]: string;13}1415export interface IGrammarContributions {16getGrammar(mode: string): string;17}1819class GrammarContributions implements IGrammarContributions {2021private static _grammars: ModeScopeMap = {};2223constructor(contributions: ExtensionPointContribution<ITMSyntaxExtensionPoint[]>[]) {24if (!Object.keys(GrammarContributions._grammars).length) {25this.fillModeScopeMap(contributions);26}27}2829private fillModeScopeMap(contributions: ExtensionPointContribution<ITMSyntaxExtensionPoint[]>[]) {30contributions.forEach((contribution) => {31contribution.value.forEach((grammar) => {32if (grammar.language && grammar.scopeName) {33GrammarContributions._grammars[grammar.language] = grammar.scopeName;34}35});36});37}3839public getGrammar(mode: string): string {40return GrammarContributions._grammars[mode];41}42}4344type IEmmetActionOptions = IActionOptions & {45actionName: string;46};4748export abstract class EmmetEditorAction extends EditorAction {4950protected emmetActionName: string;5152constructor(opts: IEmmetActionOptions) {53super(opts);54this.emmetActionName = opts.actionName;55}5657private static readonly emmetSupportedModes = ['html', 'css', 'xml', 'xsl', 'haml', 'jade', 'jsx', 'slim', 'scss', 'sass', 'less', 'stylus', 'styl', 'svg'];5859private _lastGrammarContributions: Promise<GrammarContributions> | null = null;60private _lastExtensionService: IExtensionService | null = null;61private _withGrammarContributions(extensionService: IExtensionService): Promise<GrammarContributions | null> {62if (this._lastExtensionService !== extensionService) {63this._lastExtensionService = extensionService;64this._lastGrammarContributions = extensionService.readExtensionPointContributions(grammarsExtPoint).then((contributions) => {65return new GrammarContributions(contributions);66});67}68return this._lastGrammarContributions || Promise.resolve(null);69}7071public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {72const extensionService = accessor.get(IExtensionService);73const commandService = accessor.get(ICommandService);7475return this._withGrammarContributions(extensionService).then((grammarContributions) => {7677if (this.id === 'editor.emmet.action.expandAbbreviation' && grammarContributions) {78return commandService.executeCommand<void>('emmet.expandAbbreviation', EmmetEditorAction.getLanguage(editor, grammarContributions));79}8081return undefined;82});8384}8586public static getLanguage(editor: ICodeEditor, grammars: IGrammarContributions) {87const model = editor.getModel();88const selection = editor.getSelection();8990if (!model || !selection) {91return null;92}9394const position = selection.getStartPosition();95model.tokenization.tokenizeIfCheap(position.lineNumber);96const languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column);97const syntax = languageId.split('.').pop();9899if (!syntax) {100return null;101}102103const checkParentMode = (): string => {104const languageGrammar = grammars.getGrammar(syntax);105if (!languageGrammar) {106return syntax;107}108const languages = languageGrammar.split('.');109if (languages.length < 2) {110return syntax;111}112for (let i = 1; i < languages.length; i++) {113const language = languages[languages.length - i];114if (this.emmetSupportedModes.indexOf(language) !== -1) {115return language;116}117}118return syntax;119};120121return {122language: syntax,123parentMode: checkParentMode()124};125}126127128}129130131