private provideCompletionItemsInternal()

in extensions/emmet/src/defaultCompletionProvider.ts [43:217]


	private provideCompletionItemsInternal(document: vscode.TextDocument, position: vscode.Position, context: vscode.CompletionContext): Thenable<vscode.CompletionList | undefined> | undefined {
		const emmetConfig = vscode.workspace.getConfiguration('emmet');
		const excludedLanguages = emmetConfig['excludeLanguages'] ? emmetConfig['excludeLanguages'] : [];
		if (excludedLanguages.indexOf(document.languageId) > -1) {
			return;
		}

		const mappedLanguages = getMappingForIncludedLanguages();
		const isSyntaxMapped = mappedLanguages[document.languageId] ? true : false;
		let emmetMode = getEmmetMode((isSyntaxMapped ? mappedLanguages[document.languageId] : document.languageId), mappedLanguages, excludedLanguages);

		if (!emmetMode
			|| emmetConfig['showExpandedAbbreviation'] === 'never'
			|| ((isSyntaxMapped || emmetMode === 'jsx') && emmetConfig['showExpandedAbbreviation'] !== 'always')) {
			return;
		}

		let syntax = emmetMode;

		let validateLocation = syntax === 'html' || syntax === 'jsx' || syntax === 'xml';
		let rootNode: Node | undefined;
		let currentNode: Node | undefined;

		const lsDoc = toLSTextDocument(document);
		position = document.validatePosition(position);

		// Don't show completions if there's a comment at the beginning of the line
		const lineRange = new vscode.Range(position.line, 0, position.line, position.character);
		if (document.getText(lineRange).trimStart().startsWith('//')) {
			return;
		}

		const helper = getEmmetHelper();
		if (syntax === 'html') {
			if (context.triggerKind === vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) {
				switch (this.lastCompletionType) {
					case 'html':
						validateLocation = false;
						break;
					case 'css':
						validateLocation = false;
						syntax = 'css';
						break;
					default:
						break;
				}
			}
			if (validateLocation) {
				const positionOffset = document.offsetAt(position);
				const emmetRootNode = getRootNode(document, true);
				const foundNode = getHtmlFlatNode(document.getText(), emmetRootNode, positionOffset, false);
				if (foundNode) {
					if (foundNode.name === 'script') {
						const typeNode = foundNode.attributes.find(attr => attr.name.toString() === 'type');
						if (typeNode) {
							const typeAttrValue = typeNode.value.toString();
							if (typeAttrValue === 'application/javascript' || typeAttrValue === 'text/javascript') {
								if (!getSyntaxFromArgs({ language: 'javascript' })) {
									return;
								} else {
									validateLocation = false;
								}
							}
							else if (allowedMimeTypesInScriptTag.includes(typeAttrValue)) {
								validateLocation = false;
							}
						} else {
							return;
						}
					}
					else if (foundNode.name === 'style') {
						syntax = 'css';
						validateLocation = false;
					} else {
						const styleNode = foundNode.attributes.find(attr => attr.name.toString() === 'style');
						if (styleNode && styleNode.value.start <= positionOffset && positionOffset <= styleNode.value.end) {
							syntax = 'css';
							validateLocation = false;
						}
					}
				}
			}
		}

		const expandOptions = isStyleSheet(syntax) ?
			{ lookAhead: false, syntax: 'stylesheet' } :
			{ lookAhead: true, syntax: 'markup' };
		const extractAbbreviationResults = helper.extractAbbreviation(lsDoc, position, expandOptions);
		if (!extractAbbreviationResults || !helper.isAbbreviationValid(syntax, extractAbbreviationResults.abbreviation)) {
			return;
		}

		const offset = document.offsetAt(position);
		if (isStyleSheet(document.languageId) && context.triggerKind !== vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) {
			validateLocation = true;
			let usePartialParsing = vscode.workspace.getConfiguration('emmet')['optimizeStylesheetParsing'] === true;
			rootNode = usePartialParsing && document.lineCount > 1000 ? parsePartialStylesheet(document, position) : <Stylesheet>getRootNode(document, true);
			if (!rootNode) {
				return;
			}
			currentNode = getFlatNode(rootNode, offset, true);
		}

		// Fix for https://github.com/microsoft/vscode/issues/107578
		// Validate location if syntax is of styleSheet type to ensure that location is valid for emmet abbreviation.
		// For an html document containing a <style> node, compute the embeddedCssNode and fetch the flattened node as currentNode.
		if (!isStyleSheet(document.languageId) && isStyleSheet(syntax) && context.triggerKind !== vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) {
			validateLocation = true;
			rootNode = getRootNode(document, true);
			if (!rootNode) {
				return;
			}
			let flatNode = getFlatNode(rootNode, offset, true);
			let embeddedCssNode = getEmbeddedCssNodeIfAny(document, flatNode, position);
			currentNode = getFlatNode(embeddedCssNode, offset, true);
		}

		if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, currentNode, syntax, offset, toRange(extractAbbreviationResults.abbreviationRange))) {
			return;
		}

		let noiseCheckPromise: Thenable<any> = Promise.resolve();

		// Fix for https://github.com/microsoft/vscode/issues/32647
		// Check for document symbols in js/ts/jsx/tsx and avoid triggering emmet for abbreviations of the form symbolName.sometext
		// Presence of > or * or + in the abbreviation denotes valid abbreviation that should trigger emmet
		if (!isStyleSheet(syntax) && (document.languageId === 'javascript' || document.languageId === 'javascriptreact' || document.languageId === 'typescript' || document.languageId === 'typescriptreact')) {
			let abbreviation: string = extractAbbreviationResults.abbreviation;
			if (abbreviation.startsWith('this.')) {
				noiseCheckPromise = Promise.resolve(true);
			} else {
				noiseCheckPromise = vscode.commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeDocumentSymbolProvider', document.uri).then((symbols: vscode.SymbolInformation[] | undefined) => {
					return symbols && symbols.find(x => abbreviation === x.name || (abbreviation.startsWith(x.name + '.') && !/>|\*|\+/.test(abbreviation)));
				});
			}
		}

		return noiseCheckPromise.then((noise): vscode.CompletionList | undefined => {
			if (noise) {
				return;
			}

			const config = getEmmetConfiguration(syntax!);
			const result = helper.doComplete(toLSTextDocument(document), position, syntax, config);

			// https://github.com/microsoft/vscode/issues/86941
			if (result && result.items && result.items.length === 1) {
				if (result.items[0].label === 'widows: ;') {
					return undefined;
				}
			}

			let newItems: vscode.CompletionItem[] = [];
			if (result && result.items) {
				result.items.forEach((item: any) => {
					let newItem = new vscode.CompletionItem(item.label);
					newItem.documentation = item.documentation;
					newItem.detail = item.detail;
					newItem.insertText = new vscode.SnippetString(item.textEdit.newText);
					let oldrange = item.textEdit.range;
					newItem.range = new vscode.Range(oldrange.start.line, oldrange.start.character, oldrange.end.line, oldrange.end.character);

					newItem.filterText = item.filterText;
					newItem.sortText = item.sortText;

					if (emmetConfig['showSuggestionsAsSnippets'] === true) {
						newItem.kind = vscode.CompletionItemKind.Snippet;
					}
					newItems.push(newItem);
				});
			}

			return new vscode.CompletionList(newItems, true);
		});
	}