extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts/visit
function collectTokens(jsLanguageService: ts.LanguageService, fileName: string, span: ts.TextSpan, collector: (node: ts.Node, tokenType: number, tokenModifier: number) => void) {

	const program = jsLanguageService.getProgram();
	if (program) {
		const typeChecker = program.getTypeChecker();

		function visit(node: ts.Node) {
			if (!node || !ts.textSpanIntersectsWith(span, node.pos, node.getFullWidth())) {
				return;
			}
			if (ts.isIdentifier(node)) {
				let symbol = typeChecker.getSymbolAtLocation(node);
				if (symbol) {
					if (symbol.flags & ts.SymbolFlags.Alias) {
						symbol = typeChecker.getAliasedSymbol(symbol);
					}
					let typeIdx = classifySymbol(symbol);
					if (typeIdx !== undefined) {
						let modifierSet = 0;
						if (node.parent) {
							const parentTypeIdx = tokenFromDeclarationMapping[node.parent.kind];
							if (parentTypeIdx === typeIdx && (<ts.NamedDeclaration>node.parent).name === node) {
								modifierSet = 1 << TokenModifier.declaration;
							}
						}
						const decl = symbol.valueDeclaration;
						const modifiers = decl ? ts.getCombinedModifierFlags(decl) : 0;
						const nodeFlags = decl ? ts.getCombinedNodeFlags(decl) : 0;
						if (modifiers & ts.ModifierFlags.Static) {
							modifierSet |= 1 << TokenModifier.static;
						}
						if (modifiers & ts.ModifierFlags.Async) {
							modifierSet |= 1 << TokenModifier.async;
						}
						if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) {
							modifierSet |= 1 << TokenModifier.readonly;
						}
						collector(node, typeIdx, modifierSet);
					}
				}
			}

			ts.forEachChild(node, visit);
		}
		const sourceFile = program.getSourceFile(fileName);
		if (sourceFile) {
			visit(sourceFile);
		}
	}
}