export function doComplete()

in src/emmetHelper.ts [65:217]


export function doComplete(document: TextDocument, position: Position, syntax: string, emmetConfig: VSCodeEmmetConfig): CompletionList | undefined {
	if (emmetConfig.showExpandedAbbreviation === 'never' || !getEmmetMode(syntax, emmetConfig.excludeLanguages)) {
		return;
	}

	const isStyleSheetRes = isStyleSheet(syntax);

	// Fetch markupSnippets so that we can provide possible abbreviation completions
	// For example, when text at position is `a`, completions should return `a:blank`, `a:link`, `acr` etc.
	if (!isStyleSheetRes) {
		if (!snippetKeyCache.has(syntax)) {
			const registry: SnippetsMap = {
				...getDefaultSnippets(syntax),
				...customSnippetsRegistry[syntax]
			};
			snippetKeyCache.set(syntax, Object.keys(registry));
		}
		markupSnippetKeys = snippetKeyCache.get(syntax) ?? [];
	}

	const extractOptions: Partial<ExtractOptions> = { lookAhead: !isStyleSheetRes, type: isStyleSheetRes ? 'stylesheet' : 'markup' };
	const extractedValue = extractAbbreviation(document, position, extractOptions);
	if (!extractedValue) {
		return;
	}
	const { abbreviationRange, abbreviation, filter } = extractedValue;
	const currentLineTillPosition = getCurrentLine(document, position).substr(0, position.character);
	const currentWord = getCurrentWord(currentLineTillPosition);

	// Don't attempt to expand open tags
	if (currentWord === abbreviation
		&& currentLineTillPosition.endsWith(`<${abbreviation}`)
		&& syntaxes.markup.includes(syntax)) {
		return;
	}

	const expandOptions = getExpandOptions(syntax, emmetConfig, filter);

	let expandedText: string = "";
	let expandedAbbr: CompletionItem | undefined;
	let completionItems: CompletionItem[] = [];

	// Create completion item after expanding given abbreviation
	// if abbreviation is valid and expanded value is not noise
	const createExpandedAbbr = (syntax: string, abbr: string) => {
		if (!isAbbreviationValid(syntax, abbreviation)) {
			return;
		}

		try {
			expandedText = expand(abbr, expandOptions);

			// manually patch https://github.com/microsoft/vscode/issues/120245 for now
			if (isStyleSheetRes && '!important'.startsWith(abbr)) {
				expandedText = '!important';
			}
		} catch (e) {
		}

		if (!expandedText || isExpandedTextNoise(syntax, abbr, expandedText, expandOptions.options)) {
			return;
		}

		expandedAbbr = CompletionItem.create(abbr);
		expandedAbbr.textEdit = TextEdit.replace(abbreviationRange, escapeNonTabStopDollar(addFinalTabStop(expandedText)));
		expandedAbbr.documentation = replaceTabStopsWithCursors(expandedText);
		expandedAbbr.insertTextFormat = InsertTextFormat.Snippet;
		expandedAbbr.detail = localize('Emmet abbreviation', "Emmet Abbreviation");
		expandedAbbr.label = abbreviation;
		expandedAbbr.label += filter ? '|' + filter.replace(',', '|') : "";
		completionItems = [expandedAbbr];
	}

	if (isStyleSheet(syntax)) {
		createExpandedAbbr(syntax, abbreviation);

		// When abbr is longer than usual emmet snippets and matches better with existing css property, then no emmet
		if (abbreviation.length > 4
			&& cssData.properties.find(x => x.startsWith(abbreviation))) {
			return CompletionList.create([], true);
		}

		if (expandedAbbr && expandedText.length) {
			expandedAbbr.textEdit = TextEdit.replace(abbreviationRange, escapeNonTabStopDollar(addFinalTabStop(expandedText)));
			expandedAbbr.documentation = replaceTabStopsWithCursors(expandedText);
			expandedAbbr.label = removeTabStops(expandedText);
			expandedAbbr.filterText = abbreviation;

			// Custom snippets should show up in completions if abbreviation is a prefix
			const stylesheetCustomSnippetsKeys = stylesheetCustomSnippetsKeyCache.has(syntax) ?
				stylesheetCustomSnippetsKeyCache.get(syntax) : stylesheetCustomSnippetsKeyCache.get('css');
			completionItems = makeSnippetSuggestion(
				stylesheetCustomSnippetsKeys ?? [],
				abbreviation,
				abbreviation,
				abbreviationRange,
				expandOptions,
				'Emmet Custom Snippet',
				false);

			if (!completionItems.find(x => x.textEdit?.newText && x.textEdit?.newText === expandedAbbr?.textEdit?.newText)) {

				// Fix for https://github.com/Microsoft/vscode/issues/28933#issuecomment-309236902
				// When user types in propertyname, emmet uses it to match with snippet names, resulting in width -> widows or font-family -> font: family
				// Filter out those cases here.
				const abbrRegex = new RegExp('.*' + abbreviation.split('').map(x => (x === '$' || x === '+') ? '\\' + x : x).join('.*') + '.*', 'i');
				if (/\d/.test(abbreviation) || abbrRegex.test(expandedAbbr.label)) {
					completionItems.push(expandedAbbr);
				}
			}
		}
	} else {
		createExpandedAbbr(syntax, abbreviation);

		let tagToFindMoreSuggestionsFor = abbreviation;
		const newTagMatches = abbreviation.match(/(>|\+)([\w:-]+)$/);
		if (newTagMatches && newTagMatches.length === 3) {
			tagToFindMoreSuggestionsFor = newTagMatches[2];
		}

		if (syntax !== 'xml') {
			const commonlyUsedTagSuggestions = makeSnippetSuggestion(commonlyUsedTags, tagToFindMoreSuggestionsFor, abbreviation, abbreviationRange, expandOptions, 'Emmet Abbreviation');
			completionItems = completionItems.concat(commonlyUsedTagSuggestions);
		}

		if (emmetConfig.showAbbreviationSuggestions === true) {
			const abbreviationSuggestions = makeSnippetSuggestion(markupSnippetKeys.filter(x => !commonlyUsedTags.includes(x)), tagToFindMoreSuggestionsFor, abbreviation, abbreviationRange, expandOptions, 'Emmet Abbreviation');

			// Workaround for the main expanded abbr not appearing before the snippet suggestions
			if (expandedAbbr && abbreviationSuggestions.length > 0 && tagToFindMoreSuggestionsFor !== abbreviation) {
				expandedAbbr.sortText = '0' + expandedAbbr.label;
				abbreviationSuggestions.forEach(item => {
					// Workaround for snippet suggestions items getting filtered out as the complete abbr does not start with snippetKey
					item.filterText = abbreviation
					// Workaround for the main expanded abbr not appearing before the snippet suggestions
					item.sortText = '9' + abbreviation;
				});
			}
			completionItems = completionItems.concat(abbreviationSuggestions);
		}

		// https://github.com/microsoft/vscode/issues/66680
		if (syntax === 'html' && completionItems.length >= 2 && abbreviation.includes(":")
			&& expandedAbbr?.textEdit?.newText === `<${abbreviation}>\${0}</${abbreviation}>`) {
			completionItems = completionItems.filter(item => item.label !== abbreviation);
		}
	}

	if (emmetConfig.showSuggestionsAsSnippets === true) {
		completionItems.forEach(x => x.kind = CompletionItemKind.Snippet);
	}
	return completionItems.length ? CompletionList.create(completionItems, true) : undefined;
}