in extensions/emmet/src/abbreviationActions.ts [229:349]
export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined> {
if (!validate() || !vscode.window.activeTextEditor) {
return fallbackTab();
}
args = args || {};
if (!args['language']) {
args['language'] = vscode.window.activeTextEditor.document.languageId;
} else {
const excludedLanguages = vscode.workspace.getConfiguration('emmet')['excludeLanguages'] ? vscode.workspace.getConfiguration('emmet')['excludeLanguages'] : [];
if (excludedLanguages.indexOf(vscode.window.activeTextEditor.document.languageId) > -1) {
return fallbackTab();
}
}
const syntax = getSyntaxFromArgs(args);
if (!syntax) {
return fallbackTab();
}
const editor = vscode.window.activeTextEditor;
let rootNode: Node | undefined;
let usePartialParsing = vscode.workspace.getConfiguration('emmet')['optimizeStylesheetParsing'] === true;
if (editor.selections.length === 1 && isStyleSheet(editor.document.languageId) && usePartialParsing && editor.document.lineCount > 1000) {
rootNode = parsePartialStylesheet(editor.document, editor.selection.isReversed ? editor.selection.anchor : editor.selection.active);
} else {
rootNode = parseDocument(editor.document, false);
}
// When tabbed on a non empty selection, do not treat it as an emmet abbreviation, and fallback to tab instead
if (vscode.workspace.getConfiguration('emmet')['triggerExpansionOnTab'] === true && editor.selections.find(x => !x.isEmpty)) {
return fallbackTab();
}
let abbreviationList: ExpandAbbreviationInput[] = [];
let firstAbbreviation: string;
let allAbbreviationsSame: boolean = true;
const helper = getEmmetHelper();
let getAbbreviation = (document: vscode.TextDocument, selection: vscode.Selection, position: vscode.Position, syntax: string): [vscode.Range | null, string, string] => {
let rangeToReplace: vscode.Range = selection;
let abbr = document.getText(rangeToReplace);
if (!rangeToReplace.isEmpty) {
let extractedResults = helper.extractAbbreviationFromText(abbr);
if (extractedResults) {
return [rangeToReplace, extractedResults.abbreviation, extractedResults.filter];
}
return [null, '', ''];
}
const currentLine = editor.document.lineAt(position.line).text;
const textTillPosition = currentLine.substr(0, position.character);
// Expand cases like <div to <div></div> explicitly
// else we will end up with <<div></div>
if (syntax === 'html') {
let matches = textTillPosition.match(/<(\w+)$/);
if (matches) {
abbr = matches[1];
rangeToReplace = new vscode.Range(position.translate(0, -(abbr.length + 1)), position);
return [rangeToReplace, abbr, ''];
}
}
let extractedResults = helper.extractAbbreviation(editor.document, position, false);
if (!extractedResults) {
return [null, '', ''];
}
let { abbreviationRange, abbreviation, filter } = extractedResults;
return [new vscode.Range(abbreviationRange.start.line, abbreviationRange.start.character, abbreviationRange.end.line, abbreviationRange.end.character), abbreviation, filter];
};
let selectionsInReverseOrder = editor.selections.slice(0);
selectionsInReverseOrder.sort((a, b) => {
const posA = a.isReversed ? a.anchor : a.active;
const posB = b.isReversed ? b.anchor : b.active;
return posA.compareTo(posB) * -1;
});
selectionsInReverseOrder.forEach(selection => {
let position = selection.isReversed ? selection.anchor : selection.active;
let [rangeToReplace, abbreviation, filter] = getAbbreviation(editor.document, selection, position, syntax);
if (!rangeToReplace) {
return;
}
if (!helper.isAbbreviationValid(syntax, abbreviation)) {
return;
}
let currentNode = getNode(rootNode, position, true);
let validateLocation = true;
let syntaxToUse = syntax;
if (editor.document.languageId === 'html') {
if (isStyleAttribute(currentNode, position)) {
syntaxToUse = 'css';
validateLocation = false;
} else {
const embeddedCssNode = getEmbeddedCssNodeIfAny(editor.document, currentNode, position);
if (embeddedCssNode) {
currentNode = getNode(embeddedCssNode, position, true);
syntaxToUse = 'css';
}
}
}
if (validateLocation && !isValidLocationForEmmetAbbreviation(editor.document, rootNode, currentNode, syntaxToUse, position, rangeToReplace)) {
return;
}
if (!firstAbbreviation) {
firstAbbreviation = abbreviation;
} else if (allAbbreviationsSame && firstAbbreviation !== abbreviation) {
allAbbreviationsSame = false;
}
abbreviationList.push({ syntax: syntaxToUse, abbreviation, rangeToReplace, filter });
});
return expandAbbreviationInRange(editor, abbreviationList, allAbbreviationsSame).then(success => {
return success ? Promise.resolve(undefined) : fallbackTab();
});
}