356f5f30962cb498c8b44152969c76afa52d89862fa9809ce75090513160afa953753acfa6abbdd5d72b85a7f1850a1a34ab89ea78ab53670301da61d120fd 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. import './inspectTokens.css';
  15. import { $, append, reset } from '../../../../base/browser/dom.js';
  16. import { Color } from '../../../../base/common/color.js';
  17. import { Disposable } from '../../../../base/common/lifecycle.js';
  18. import { EditorAction, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js';
  19. import { TokenizationRegistry } from '../../../common/languages.js';
  20. import { TokenMetadata } from '../../../common/encodedTokenAttributes.js';
  21. import { NullState, nullTokenize, nullTokenizeEncoded } from '../../../common/languages/nullTokenize.js';
  22. import { ILanguageService } from '../../../common/languages/language.js';
  23. import { IStandaloneThemeService } from '../../common/standaloneTheme.js';
  24. import { editorHoverBackground, editorHoverBorder, editorHoverForeground } from '../../../../platform/theme/common/colorRegistry.js';
  25. import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
  26. import { InspectTokensNLS } from '../../../common/standaloneStrings.js';
  27. import { isHighContrast } from '../../../../platform/theme/common/theme.js';
  28. let InspectTokensController = class InspectTokensController extends Disposable {
  29. constructor(editor, standaloneColorService, languageService) {
  30. super();
  31. this._editor = editor;
  32. this._languageService = languageService;
  33. this._widget = null;
  34. this._register(this._editor.onDidChangeModel((e) => this.stop()));
  35. this._register(this._editor.onDidChangeModelLanguage((e) => this.stop()));
  36. this._register(TokenizationRegistry.onDidChange((e) => this.stop()));
  37. this._register(this._editor.onKeyUp((e) => e.keyCode === 9 /* KeyCode.Escape */ && this.stop()));
  38. }
  39. static get(editor) {
  40. return editor.getContribution(InspectTokensController.ID);
  41. }
  42. dispose() {
  43. this.stop();
  44. super.dispose();
  45. }
  46. launch() {
  47. if (this._widget) {
  48. return;
  49. }
  50. if (!this._editor.hasModel()) {
  51. return;
  52. }
  53. this._widget = new InspectTokensWidget(this._editor, this._languageService);
  54. }
  55. stop() {
  56. if (this._widget) {
  57. this._widget.dispose();
  58. this._widget = null;
  59. }
  60. }
  61. };
  62. InspectTokensController.ID = 'editor.contrib.inspectTokens';
  63. InspectTokensController = __decorate([
  64. __param(1, IStandaloneThemeService),
  65. __param(2, ILanguageService)
  66. ], InspectTokensController);
  67. class InspectTokens extends EditorAction {
  68. constructor() {
  69. super({
  70. id: 'editor.action.inspectTokens',
  71. label: InspectTokensNLS.inspectTokensAction,
  72. alias: 'Developer: Inspect Tokens',
  73. precondition: undefined
  74. });
  75. }
  76. run(accessor, editor) {
  77. const controller = InspectTokensController.get(editor);
  78. if (controller) {
  79. controller.launch();
  80. }
  81. }
  82. }
  83. function renderTokenText(tokenText) {
  84. let result = '';
  85. for (let charIndex = 0, len = tokenText.length; charIndex < len; charIndex++) {
  86. const charCode = tokenText.charCodeAt(charIndex);
  87. switch (charCode) {
  88. case 9 /* CharCode.Tab */:
  89. result += '\u2192'; // &rarr;
  90. break;
  91. case 32 /* CharCode.Space */:
  92. result += '\u00B7'; // &middot;
  93. break;
  94. default:
  95. result += String.fromCharCode(charCode);
  96. }
  97. }
  98. return result;
  99. }
  100. function getSafeTokenizationSupport(languageIdCodec, languageId) {
  101. const tokenizationSupport = TokenizationRegistry.get(languageId);
  102. if (tokenizationSupport) {
  103. return tokenizationSupport;
  104. }
  105. const encodedLanguageId = languageIdCodec.encodeLanguageId(languageId);
  106. return {
  107. getInitialState: () => NullState,
  108. tokenize: (line, hasEOL, state) => nullTokenize(languageId, state),
  109. tokenizeEncoded: (line, hasEOL, state) => nullTokenizeEncoded(encodedLanguageId, state)
  110. };
  111. }
  112. class InspectTokensWidget extends Disposable {
  113. constructor(editor, languageService) {
  114. super();
  115. // Editor.IContentWidget.allowEditorOverflow
  116. this.allowEditorOverflow = true;
  117. this._editor = editor;
  118. this._languageService = languageService;
  119. this._model = this._editor.getModel();
  120. this._domNode = document.createElement('div');
  121. this._domNode.className = 'tokens-inspect-widget';
  122. this._tokenizationSupport = getSafeTokenizationSupport(this._languageService.languageIdCodec, this._model.getLanguageId());
  123. this._compute(this._editor.getPosition());
  124. this._register(this._editor.onDidChangeCursorPosition((e) => this._compute(this._editor.getPosition())));
  125. this._editor.addContentWidget(this);
  126. }
  127. dispose() {
  128. this._editor.removeContentWidget(this);
  129. super.dispose();
  130. }
  131. getId() {
  132. return InspectTokensWidget._ID;
  133. }
  134. _compute(position) {
  135. const data = this._getTokensAtLine(position.lineNumber);
  136. let token1Index = 0;
  137. for (let i = data.tokens1.length - 1; i >= 0; i--) {
  138. const t = data.tokens1[i];
  139. if (position.column - 1 >= t.offset) {
  140. token1Index = i;
  141. break;
  142. }
  143. }
  144. let token2Index = 0;
  145. for (let i = (data.tokens2.length >>> 1); i >= 0; i--) {
  146. if (position.column - 1 >= data.tokens2[(i << 1)]) {
  147. token2Index = i;
  148. break;
  149. }
  150. }
  151. const lineContent = this._model.getLineContent(position.lineNumber);
  152. let tokenText = '';
  153. if (token1Index < data.tokens1.length) {
  154. const tokenStartIndex = data.tokens1[token1Index].offset;
  155. const tokenEndIndex = token1Index + 1 < data.tokens1.length ? data.tokens1[token1Index + 1].offset : lineContent.length;
  156. tokenText = lineContent.substring(tokenStartIndex, tokenEndIndex);
  157. }
  158. reset(this._domNode, $('h2.tm-token', undefined, renderTokenText(tokenText), $('span.tm-token-length', undefined, `${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'}`)));
  159. append(this._domNode, $('hr.tokens-inspect-separator', { 'style': 'clear:both' }));
  160. const metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null;
  161. append(this._domNode, $('table.tm-metadata-table', undefined, $('tbody', undefined, $('tr', undefined, $('td.tm-metadata-key', undefined, 'language'), $('td.tm-metadata-value', undefined, `${metadata ? metadata.languageId : '-?-'}`)), $('tr', undefined, $('td.tm-metadata-key', undefined, 'token type'), $('td.tm-metadata-value', undefined, `${metadata ? this._tokenTypeToString(metadata.tokenType) : '-?-'}`)), $('tr', undefined, $('td.tm-metadata-key', undefined, 'font style'), $('td.tm-metadata-value', undefined, `${metadata ? this._fontStyleToString(metadata.fontStyle) : '-?-'}`)), $('tr', undefined, $('td.tm-metadata-key', undefined, 'foreground'), $('td.tm-metadata-value', undefined, `${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : '-?-'}`)), $('tr', undefined, $('td.tm-metadata-key', undefined, 'background'), $('td.tm-metadata-value', undefined, `${metadata ? Color.Format.CSS.formatHex(metadata.background) : '-?-'}`)))));
  162. append(this._domNode, $('hr.tokens-inspect-separator'));
  163. if (token1Index < data.tokens1.length) {
  164. append(this._domNode, $('span.tm-token-type', undefined, data.tokens1[token1Index].type));
  165. }
  166. this._editor.layoutContentWidget(this);
  167. }
  168. _decodeMetadata(metadata) {
  169. const colorMap = TokenizationRegistry.getColorMap();
  170. const languageId = TokenMetadata.getLanguageId(metadata);
  171. const tokenType = TokenMetadata.getTokenType(metadata);
  172. const fontStyle = TokenMetadata.getFontStyle(metadata);
  173. const foreground = TokenMetadata.getForeground(metadata);
  174. const background = TokenMetadata.getBackground(metadata);
  175. return {
  176. languageId: this._languageService.languageIdCodec.decodeLanguageId(languageId),
  177. tokenType: tokenType,
  178. fontStyle: fontStyle,
  179. foreground: colorMap[foreground],
  180. background: colorMap[background]
  181. };
  182. }
  183. _tokenTypeToString(tokenType) {
  184. switch (tokenType) {
  185. case 0 /* StandardTokenType.Other */: return 'Other';
  186. case 1 /* StandardTokenType.Comment */: return 'Comment';
  187. case 2 /* StandardTokenType.String */: return 'String';
  188. case 3 /* StandardTokenType.RegEx */: return 'RegEx';
  189. default: return '??';
  190. }
  191. }
  192. _fontStyleToString(fontStyle) {
  193. let r = '';
  194. if (fontStyle & 1 /* FontStyle.Italic */) {
  195. r += 'italic ';
  196. }
  197. if (fontStyle & 2 /* FontStyle.Bold */) {
  198. r += 'bold ';
  199. }
  200. if (fontStyle & 4 /* FontStyle.Underline */) {
  201. r += 'underline ';
  202. }
  203. if (fontStyle & 8 /* FontStyle.Strikethrough */) {
  204. r += 'strikethrough ';
  205. }
  206. if (r.length === 0) {
  207. r = '---';
  208. }
  209. return r;
  210. }
  211. _getTokensAtLine(lineNumber) {
  212. const stateBeforeLine = this._getStateBeforeLine(lineNumber);
  213. const tokenizationResult1 = this._tokenizationSupport.tokenize(this._model.getLineContent(lineNumber), true, stateBeforeLine);
  214. const tokenizationResult2 = this._tokenizationSupport.tokenizeEncoded(this._model.getLineContent(lineNumber), true, stateBeforeLine);
  215. return {
  216. startState: stateBeforeLine,
  217. tokens1: tokenizationResult1.tokens,
  218. tokens2: tokenizationResult2.tokens,
  219. endState: tokenizationResult1.endState
  220. };
  221. }
  222. _getStateBeforeLine(lineNumber) {
  223. let state = this._tokenizationSupport.getInitialState();
  224. for (let i = 1; i < lineNumber; i++) {
  225. const tokenizationResult = this._tokenizationSupport.tokenize(this._model.getLineContent(i), true, state);
  226. state = tokenizationResult.endState;
  227. }
  228. return state;
  229. }
  230. getDomNode() {
  231. return this._domNode;
  232. }
  233. getPosition() {
  234. return {
  235. position: this._editor.getPosition(),
  236. preference: [2 /* ContentWidgetPositionPreference.BELOW */, 1 /* ContentWidgetPositionPreference.ABOVE */]
  237. };
  238. }
  239. }
  240. InspectTokensWidget._ID = 'editor.contrib.inspectTokensWidget';
  241. registerEditorContribution(InspectTokensController.ID, InspectTokensController);
  242. registerEditorAction(InspectTokens);
  243. registerThemingParticipant((theme, collector) => {
  244. const border = theme.getColor(editorHoverBorder);
  245. if (border) {
  246. const borderWidth = isHighContrast(theme.type) ? 2 : 1;
  247. collector.addRule(`.monaco-editor .tokens-inspect-widget { border: ${borderWidth}px solid ${border}; }`);
  248. collector.addRule(`.monaco-editor .tokens-inspect-widget .tokens-inspect-separator { background-color: ${border}; }`);
  249. }
  250. const background = theme.getColor(editorHoverBackground);
  251. if (background) {
  252. collector.addRule(`.monaco-editor .tokens-inspect-widget { background-color: ${background}; }`);
  253. }
  254. const foreground = theme.getColor(editorHoverForeground);
  255. if (foreground) {
  256. collector.addRule(`.monaco-editor .tokens-inspect-widget { color: ${foreground}; }`);
  257. }
  258. });