ee3aa94e93ba19f628ce426cafbd700466e2a0fc850e91941b61aba585ceff6c2680902653238db1c9c69c5224fb28228413377e257e2fe08e3f11c65c9d94 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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 * as dom from '../../../../base/browser/dom.js';
  15. import { ScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js';
  16. import { isNonEmptyArray } from '../../../../base/common/arrays.js';
  17. import { Color } from '../../../../base/common/color.js';
  18. import { Emitter } from '../../../../base/common/event.js';
  19. import { DisposableStore, dispose } from '../../../../base/common/lifecycle.js';
  20. import { basename } from '../../../../base/common/resources.js';
  21. import { splitLines } from '../../../../base/common/strings.js';
  22. import './media/gotoErrorWidget.css';
  23. import { Range } from '../../../common/core/range.js';
  24. import { peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from '../../peekView/browser/peekView.js';
  25. import * as nls from '../../../../nls.js';
  26. import { createAndFillInActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
  27. import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js';
  28. import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
  29. import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
  30. import { ILabelService } from '../../../../platform/label/common/label.js';
  31. import { MarkerSeverity } from '../../../../platform/markers/common/markers.js';
  32. import { IOpenerService } from '../../../../platform/opener/common/opener.js';
  33. import { SeverityIcon } from '../../../../platform/severityIcon/common/severityIcon.js';
  34. import { contrastBorder, editorBackground, editorErrorBorder, editorErrorForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, oneOf, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js';
  35. import { IThemeService } from '../../../../platform/theme/common/themeService.js';
  36. class MessageWidget {
  37. constructor(parent, editor, onRelatedInformation, _openerService, _labelService) {
  38. this._openerService = _openerService;
  39. this._labelService = _labelService;
  40. this._lines = 0;
  41. this._longestLineLength = 0;
  42. this._relatedDiagnostics = new WeakMap();
  43. this._disposables = new DisposableStore();
  44. this._editor = editor;
  45. const domNode = document.createElement('div');
  46. domNode.className = 'descriptioncontainer';
  47. this._messageBlock = document.createElement('div');
  48. this._messageBlock.classList.add('message');
  49. this._messageBlock.setAttribute('aria-live', 'assertive');
  50. this._messageBlock.setAttribute('role', 'alert');
  51. domNode.appendChild(this._messageBlock);
  52. this._relatedBlock = document.createElement('div');
  53. domNode.appendChild(this._relatedBlock);
  54. this._disposables.add(dom.addStandardDisposableListener(this._relatedBlock, 'click', event => {
  55. event.preventDefault();
  56. const related = this._relatedDiagnostics.get(event.target);
  57. if (related) {
  58. onRelatedInformation(related);
  59. }
  60. }));
  61. this._scrollable = new ScrollableElement(domNode, {
  62. horizontal: 1 /* ScrollbarVisibility.Auto */,
  63. vertical: 1 /* ScrollbarVisibility.Auto */,
  64. useShadows: false,
  65. horizontalScrollbarSize: 6,
  66. verticalScrollbarSize: 6
  67. });
  68. parent.appendChild(this._scrollable.getDomNode());
  69. this._disposables.add(this._scrollable.onScroll(e => {
  70. domNode.style.left = `-${e.scrollLeft}px`;
  71. domNode.style.top = `-${e.scrollTop}px`;
  72. }));
  73. this._disposables.add(this._scrollable);
  74. }
  75. dispose() {
  76. dispose(this._disposables);
  77. }
  78. update(marker) {
  79. const { source, message, relatedInformation, code } = marker;
  80. let sourceAndCodeLength = ((source === null || source === void 0 ? void 0 : source.length) || 0) + '()'.length;
  81. if (code) {
  82. if (typeof code === 'string') {
  83. sourceAndCodeLength += code.length;
  84. }
  85. else {
  86. sourceAndCodeLength += code.value.length;
  87. }
  88. }
  89. const lines = splitLines(message);
  90. this._lines = lines.length;
  91. this._longestLineLength = 0;
  92. for (const line of lines) {
  93. this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength);
  94. }
  95. dom.clearNode(this._messageBlock);
  96. this._messageBlock.setAttribute('aria-label', this.getAriaLabel(marker));
  97. this._editor.applyFontInfo(this._messageBlock);
  98. let lastLineElement = this._messageBlock;
  99. for (const line of lines) {
  100. lastLineElement = document.createElement('div');
  101. lastLineElement.innerText = line;
  102. if (line === '') {
  103. lastLineElement.style.height = this._messageBlock.style.lineHeight;
  104. }
  105. this._messageBlock.appendChild(lastLineElement);
  106. }
  107. if (source || code) {
  108. const detailsElement = document.createElement('span');
  109. detailsElement.classList.add('details');
  110. lastLineElement.appendChild(detailsElement);
  111. if (source) {
  112. const sourceElement = document.createElement('span');
  113. sourceElement.innerText = source;
  114. sourceElement.classList.add('source');
  115. detailsElement.appendChild(sourceElement);
  116. }
  117. if (code) {
  118. if (typeof code === 'string') {
  119. const codeElement = document.createElement('span');
  120. codeElement.innerText = `(${code})`;
  121. codeElement.classList.add('code');
  122. detailsElement.appendChild(codeElement);
  123. }
  124. else {
  125. this._codeLink = dom.$('a.code-link');
  126. this._codeLink.setAttribute('href', `${code.target.toString()}`);
  127. this._codeLink.onclick = (e) => {
  128. this._openerService.open(code.target, { allowCommands: true });
  129. e.preventDefault();
  130. e.stopPropagation();
  131. };
  132. const codeElement = dom.append(this._codeLink, dom.$('span'));
  133. codeElement.innerText = code.value;
  134. detailsElement.appendChild(this._codeLink);
  135. }
  136. }
  137. }
  138. dom.clearNode(this._relatedBlock);
  139. this._editor.applyFontInfo(this._relatedBlock);
  140. if (isNonEmptyArray(relatedInformation)) {
  141. const relatedInformationNode = this._relatedBlock.appendChild(document.createElement('div'));
  142. relatedInformationNode.style.paddingTop = `${Math.floor(this._editor.getOption(61 /* EditorOption.lineHeight */) * 0.66)}px`;
  143. this._lines += 1;
  144. for (const related of relatedInformation) {
  145. const container = document.createElement('div');
  146. const relatedResource = document.createElement('a');
  147. relatedResource.classList.add('filename');
  148. relatedResource.innerText = `${this._labelService.getUriBasenameLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `;
  149. relatedResource.title = this._labelService.getUriLabel(related.resource);
  150. this._relatedDiagnostics.set(relatedResource, related);
  151. const relatedMessage = document.createElement('span');
  152. relatedMessage.innerText = related.message;
  153. container.appendChild(relatedResource);
  154. container.appendChild(relatedMessage);
  155. this._lines += 1;
  156. relatedInformationNode.appendChild(container);
  157. }
  158. }
  159. const fontInfo = this._editor.getOption(46 /* EditorOption.fontInfo */);
  160. const scrollWidth = Math.ceil(fontInfo.typicalFullwidthCharacterWidth * this._longestLineLength * 0.75);
  161. const scrollHeight = fontInfo.lineHeight * this._lines;
  162. this._scrollable.setScrollDimensions({ scrollWidth, scrollHeight });
  163. }
  164. layout(height, width) {
  165. this._scrollable.getDomNode().style.height = `${height}px`;
  166. this._scrollable.getDomNode().style.width = `${width}px`;
  167. this._scrollable.setScrollDimensions({ width, height });
  168. }
  169. getHeightInLines() {
  170. return Math.min(17, this._lines);
  171. }
  172. getAriaLabel(marker) {
  173. let severityLabel = '';
  174. switch (marker.severity) {
  175. case MarkerSeverity.Error:
  176. severityLabel = nls.localize('Error', "Error");
  177. break;
  178. case MarkerSeverity.Warning:
  179. severityLabel = nls.localize('Warning', "Warning");
  180. break;
  181. case MarkerSeverity.Info:
  182. severityLabel = nls.localize('Info', "Info");
  183. break;
  184. case MarkerSeverity.Hint:
  185. severityLabel = nls.localize('Hint', "Hint");
  186. break;
  187. }
  188. let ariaLabel = nls.localize('marker aria', "{0} at {1}. ", severityLabel, marker.startLineNumber + ':' + marker.startColumn);
  189. const model = this._editor.getModel();
  190. if (model && (marker.startLineNumber <= model.getLineCount()) && (marker.startLineNumber >= 1)) {
  191. const lineContent = model.getLineContent(marker.startLineNumber);
  192. ariaLabel = `${lineContent}, ${ariaLabel}`;
  193. }
  194. return ariaLabel;
  195. }
  196. }
  197. let MarkerNavigationWidget = class MarkerNavigationWidget extends PeekViewWidget {
  198. constructor(editor, _themeService, _openerService, _menuService, instantiationService, _contextKeyService, _labelService) {
  199. super(editor, { showArrow: true, showFrame: true, isAccessible: true, frameWidth: 1 }, instantiationService);
  200. this._themeService = _themeService;
  201. this._openerService = _openerService;
  202. this._menuService = _menuService;
  203. this._contextKeyService = _contextKeyService;
  204. this._labelService = _labelService;
  205. this._callOnDispose = new DisposableStore();
  206. this._onDidSelectRelatedInformation = new Emitter();
  207. this.onDidSelectRelatedInformation = this._onDidSelectRelatedInformation.event;
  208. this._severity = MarkerSeverity.Warning;
  209. this._backgroundColor = Color.white;
  210. this._applyTheme(_themeService.getColorTheme());
  211. this._callOnDispose.add(_themeService.onDidColorThemeChange(this._applyTheme.bind(this)));
  212. this.create();
  213. }
  214. _applyTheme(theme) {
  215. this._backgroundColor = theme.getColor(editorMarkerNavigationBackground);
  216. let colorId = editorMarkerNavigationError;
  217. let headerBackground = editorMarkerNavigationErrorHeader;
  218. if (this._severity === MarkerSeverity.Warning) {
  219. colorId = editorMarkerNavigationWarning;
  220. headerBackground = editorMarkerNavigationWarningHeader;
  221. }
  222. else if (this._severity === MarkerSeverity.Info) {
  223. colorId = editorMarkerNavigationInfo;
  224. headerBackground = editorMarkerNavigationInfoHeader;
  225. }
  226. const frameColor = theme.getColor(colorId);
  227. const headerBg = theme.getColor(headerBackground);
  228. this.style({
  229. arrowColor: frameColor,
  230. frameColor: frameColor,
  231. headerBackgroundColor: headerBg,
  232. primaryHeadingColor: theme.getColor(peekViewTitleForeground),
  233. secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground)
  234. }); // style() will trigger _applyStyles
  235. }
  236. _applyStyles() {
  237. if (this._parentContainer) {
  238. this._parentContainer.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : '';
  239. }
  240. super._applyStyles();
  241. }
  242. dispose() {
  243. this._callOnDispose.dispose();
  244. super.dispose();
  245. }
  246. _fillHead(container) {
  247. super._fillHead(container);
  248. this._disposables.add(this._actionbarWidget.actionRunner.onBeforeRun(e => this.editor.focus()));
  249. const actions = [];
  250. const menu = this._menuService.createMenu(MarkerNavigationWidget.TitleMenu, this._contextKeyService);
  251. createAndFillInActionBarActions(menu, undefined, actions);
  252. this._actionbarWidget.push(actions, { label: false, icon: true, index: 0 });
  253. menu.dispose();
  254. }
  255. _fillTitleIcon(container) {
  256. this._icon = dom.append(container, dom.$(''));
  257. }
  258. _fillBody(container) {
  259. this._parentContainer = container;
  260. container.classList.add('marker-widget');
  261. this._parentContainer.tabIndex = 0;
  262. this._parentContainer.setAttribute('role', 'tooltip');
  263. this._container = document.createElement('div');
  264. container.appendChild(this._container);
  265. this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService, this._labelService);
  266. this._disposables.add(this._message);
  267. }
  268. show() {
  269. throw new Error('call showAtMarker');
  270. }
  271. showAtMarker(marker, markerIdx, markerCount) {
  272. // update:
  273. // * title
  274. // * message
  275. this._container.classList.remove('stale');
  276. this._message.update(marker);
  277. // update frame color (only applied on 'show')
  278. this._severity = marker.severity;
  279. this._applyTheme(this._themeService.getColorTheme());
  280. // show
  281. const range = Range.lift(marker);
  282. const editorPosition = this.editor.getPosition();
  283. const position = editorPosition && range.containsPosition(editorPosition) ? editorPosition : range.getStartPosition();
  284. super.show(position, this.computeRequiredHeight());
  285. const model = this.editor.getModel();
  286. if (model) {
  287. const detail = markerCount > 1
  288. ? nls.localize('problems', "{0} of {1} problems", markerIdx, markerCount)
  289. : nls.localize('change', "{0} of {1} problem", markerIdx, markerCount);
  290. this.setTitle(basename(model.uri), detail);
  291. }
  292. this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`;
  293. this.editor.revealPositionNearTop(position, 0 /* ScrollType.Smooth */);
  294. this.editor.focus();
  295. }
  296. updateMarker(marker) {
  297. this._container.classList.remove('stale');
  298. this._message.update(marker);
  299. }
  300. showStale() {
  301. this._container.classList.add('stale');
  302. this._relayout();
  303. }
  304. _doLayoutBody(heightInPixel, widthInPixel) {
  305. super._doLayoutBody(heightInPixel, widthInPixel);
  306. this._heightInPixel = heightInPixel;
  307. this._message.layout(heightInPixel, widthInPixel);
  308. this._container.style.height = `${heightInPixel}px`;
  309. }
  310. _onWidth(widthInPixel) {
  311. this._message.layout(this._heightInPixel, widthInPixel);
  312. }
  313. _relayout() {
  314. super._relayout(this.computeRequiredHeight());
  315. }
  316. computeRequiredHeight() {
  317. return 3 + this._message.getHeightInLines();
  318. }
  319. };
  320. MarkerNavigationWidget.TitleMenu = new MenuId('gotoErrorTitleMenu');
  321. MarkerNavigationWidget = __decorate([
  322. __param(1, IThemeService),
  323. __param(2, IOpenerService),
  324. __param(3, IMenuService),
  325. __param(4, IInstantiationService),
  326. __param(5, IContextKeyService),
  327. __param(6, ILabelService)
  328. ], MarkerNavigationWidget);
  329. export { MarkerNavigationWidget };
  330. // theming
  331. const errorDefault = oneOf(editorErrorForeground, editorErrorBorder);
  332. const warningDefault = oneOf(editorWarningForeground, editorWarningBorder);
  333. const infoDefault = oneOf(editorInfoForeground, editorInfoBorder);
  334. export const editorMarkerNavigationError = registerColor('editorMarkerNavigationError.background', { dark: errorDefault, light: errorDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationError', 'Editor marker navigation widget error color.'));
  335. export const editorMarkerNavigationErrorHeader = registerColor('editorMarkerNavigationError.headerBackground', { dark: transparent(editorMarkerNavigationError, .1), light: transparent(editorMarkerNavigationError, .1), hcDark: null, hcLight: null }, nls.localize('editorMarkerNavigationErrorHeaderBackground', 'Editor marker navigation widget error heading background.'));
  336. export const editorMarkerNavigationWarning = registerColor('editorMarkerNavigationWarning.background', { dark: warningDefault, light: warningDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationWarning', 'Editor marker navigation widget warning color.'));
  337. export const editorMarkerNavigationWarningHeader = registerColor('editorMarkerNavigationWarning.headerBackground', { dark: transparent(editorMarkerNavigationWarning, .1), light: transparent(editorMarkerNavigationWarning, .1), hcDark: '#0C141F', hcLight: transparent(editorMarkerNavigationWarning, .2) }, nls.localize('editorMarkerNavigationWarningBackground', 'Editor marker navigation widget warning heading background.'));
  338. export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationInfo.background', { dark: infoDefault, light: infoDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationInfo', 'Editor marker navigation widget info color.'));
  339. export const editorMarkerNavigationInfoHeader = registerColor('editorMarkerNavigationInfo.headerBackground', { dark: transparent(editorMarkerNavigationInfo, .1), light: transparent(editorMarkerNavigationInfo, .1), hcDark: null, hcLight: null }, nls.localize('editorMarkerNavigationInfoHeaderBackground', 'Editor marker navigation widget info heading background.'));
  340. export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: editorBackground, light: editorBackground, hcDark: editorBackground, hcLight: editorBackground }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));