Path: blob/main/src/vs/editor/common/languages/textToHtmlTokenizer.ts
3294 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 { CharCode } from '../../../base/common/charCode.js';6import * as strings from '../../../base/common/strings.js';7import { IViewLineTokens, LineTokens } from '../tokens/lineTokens.js';8import { ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from '../languages.js';9import { LanguageId } from '../encodedTokenAttributes.js';10import { NullState, nullTokenizeEncoded } from './nullTokenize.js';11import { ILanguageService } from './language.js';1213export type IReducedTokenizationSupport = Omit<ITokenizationSupport, 'tokenize'>;1415const fallback: IReducedTokenizationSupport = {16getInitialState: () => NullState,17tokenizeEncoded: (buffer: string, hasEOL: boolean, state: IState) => nullTokenizeEncoded(LanguageId.Null, state)18};1920export function tokenizeToStringSync(languageService: ILanguageService, text: string, languageId: string): string {21return _tokenizeToString(text, languageService.languageIdCodec, TokenizationRegistry.get(languageId) || fallback);22}2324export async function tokenizeToString(languageService: ILanguageService, text: string, languageId: string | null): Promise<string> {25if (!languageId) {26return _tokenizeToString(text, languageService.languageIdCodec, fallback);27}28const tokenizationSupport = await TokenizationRegistry.getOrCreate(languageId);29return _tokenizeToString(text, languageService.languageIdCodec, tokenizationSupport || fallback);30}3132export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens, colorMap: string[], startOffset: number, endOffset: number, tabSize: number, useNbsp: boolean): string {33let result = `<div>`;34let charIndex = startOffset;35let tabsCharDelta = 0;3637let prevIsSpace = true;3839for (let tokenIndex = 0, tokenCount = viewLineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) {40const tokenEndIndex = viewLineTokens.getEndOffset(tokenIndex);4142if (tokenEndIndex <= startOffset) {43continue;44}4546let partContent = '';4748for (; charIndex < tokenEndIndex && charIndex < endOffset; charIndex++) {49const charCode = text.charCodeAt(charIndex);5051switch (charCode) {52case CharCode.Tab: {53let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;54tabsCharDelta += insertSpacesCount - 1;55while (insertSpacesCount > 0) {56if (useNbsp && prevIsSpace) {57partContent += ' ';58prevIsSpace = false;59} else {60partContent += ' ';61prevIsSpace = true;62}63insertSpacesCount--;64}65break;66}67case CharCode.LessThan:68partContent += '<';69prevIsSpace = false;70break;7172case CharCode.GreaterThan:73partContent += '>';74prevIsSpace = false;75break;7677case CharCode.Ampersand:78partContent += '&';79prevIsSpace = false;80break;8182case CharCode.Null:83partContent += '�';84prevIsSpace = false;85break;8687case CharCode.UTF8_BOM:88case CharCode.LINE_SEPARATOR:89case CharCode.PARAGRAPH_SEPARATOR:90case CharCode.NEXT_LINE:91partContent += '\ufffd';92prevIsSpace = false;93break;9495case CharCode.CarriageReturn:96// zero width space, because carriage return would introduce a line break97partContent += '​';98prevIsSpace = false;99break;100101case CharCode.Space:102if (useNbsp && prevIsSpace) {103partContent += ' ';104prevIsSpace = false;105} else {106partContent += ' ';107prevIsSpace = true;108}109break;110111default:112partContent += String.fromCharCode(charCode);113prevIsSpace = false;114}115}116117result += `<span style="${viewLineTokens.getInlineStyle(tokenIndex, colorMap)}">${partContent}</span>`;118119if (tokenEndIndex > endOffset || charIndex >= endOffset) {120break;121}122}123124result += `</div>`;125return result;126}127128export function _tokenizeToString(text: string, languageIdCodec: ILanguageIdCodec, tokenizationSupport: IReducedTokenizationSupport): string {129let result = `<div class="monaco-tokenized-source">`;130const lines = strings.splitLines(text);131let currentState = tokenizationSupport.getInitialState();132for (let i = 0, len = lines.length; i < len; i++) {133const line = lines[i];134135if (i > 0) {136result += `<br/>`;137}138139const tokenizationResult = tokenizationSupport.tokenizeEncoded(line, true, currentState);140LineTokens.convertToEndOffset(tokenizationResult.tokens, line.length);141const lineTokens = new LineTokens(tokenizationResult.tokens, line, languageIdCodec);142const viewLineTokens = lineTokens.inflate();143144let startOffset = 0;145for (let j = 0, lenJ = viewLineTokens.getCount(); j < lenJ; j++) {146const type = viewLineTokens.getClassName(j);147const endIndex = viewLineTokens.getEndOffset(j);148result += `<span class="${type}">${strings.escape(line.substring(startOffset, endIndex))}</span>`;149startOffset = endIndex;150}151152currentState = tokenizationResult.endState;153}154155result += `</div>`;156return result;157}158159160