0b70a1d8c5b68e993caf75867075336a8c8673ea239090c323e995cee715db68b15c16f8b24cb449cc48cde43b7b8b66b7d7b710a755ee80346f672307d576 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 { isNonEmptyArray } from '../../../../base/common/arrays.js';
  16. import { createCancelablePromise, disposableTimeout } from '../../../../base/common/async.js';
  17. import { onUnexpectedError } from '../../../../base/common/errors.js';
  18. import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
  19. import { basename } from '../../../../base/common/resources.js';
  20. import { Range } from '../../../common/core/range.js';
  21. import { IMarkerDecorationsService } from '../../../common/services/markerDecorations.js';
  22. import { getCodeActions } from '../../codeAction/browser/codeAction.js';
  23. import { QuickFixAction, QuickFixController } from '../../codeAction/browser/codeActionCommands.js';
  24. import { CodeActionKind, CodeActionTriggerSource } from '../../codeAction/browser/types.js';
  25. import { MarkerController, NextMarkerAction } from '../../gotoError/browser/gotoError.js';
  26. import * as nls from '../../../../nls.js';
  27. import { IMarkerData, MarkerSeverity } from '../../../../platform/markers/common/markers.js';
  28. import { IOpenerService } from '../../../../platform/opener/common/opener.js';
  29. import { Progress } from '../../../../platform/progress/common/progress.js';
  30. import { textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js';
  31. import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
  32. import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
  33. const $ = dom.$;
  34. export class MarkerHover {
  35. constructor(owner, range, marker) {
  36. this.owner = owner;
  37. this.range = range;
  38. this.marker = marker;
  39. }
  40. isValidForHoverAnchor(anchor) {
  41. return (anchor.type === 1 /* HoverAnchorType.Range */
  42. && this.range.startColumn <= anchor.range.startColumn
  43. && this.range.endColumn >= anchor.range.endColumn);
  44. }
  45. }
  46. const markerCodeActionTrigger = {
  47. type: 1 /* CodeActionTriggerType.Invoke */,
  48. filter: { include: CodeActionKind.QuickFix },
  49. triggerAction: CodeActionTriggerSource.QuickFixHover
  50. };
  51. let MarkerHoverParticipant = class MarkerHoverParticipant {
  52. constructor(_editor, _markerDecorationsService, _openerService, _languageFeaturesService) {
  53. this._editor = _editor;
  54. this._markerDecorationsService = _markerDecorationsService;
  55. this._openerService = _openerService;
  56. this._languageFeaturesService = _languageFeaturesService;
  57. this.hoverOrdinal = 5;
  58. this.recentMarkerCodeActionsInfo = undefined;
  59. }
  60. computeSync(anchor, lineDecorations) {
  61. if (!this._editor.hasModel() || anchor.type !== 1 /* HoverAnchorType.Range */) {
  62. return [];
  63. }
  64. const model = this._editor.getModel();
  65. const lineNumber = anchor.range.startLineNumber;
  66. const maxColumn = model.getLineMaxColumn(lineNumber);
  67. const result = [];
  68. for (const d of lineDecorations) {
  69. const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
  70. const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
  71. const marker = this._markerDecorationsService.getMarker(model.uri, d);
  72. if (!marker) {
  73. continue;
  74. }
  75. const range = new Range(anchor.range.startLineNumber, startColumn, anchor.range.startLineNumber, endColumn);
  76. result.push(new MarkerHover(this, range, marker));
  77. }
  78. return result;
  79. }
  80. renderHoverParts(context, hoverParts) {
  81. if (!hoverParts.length) {
  82. return Disposable.None;
  83. }
  84. const disposables = new DisposableStore();
  85. hoverParts.forEach(msg => context.fragment.appendChild(this.renderMarkerHover(msg, disposables)));
  86. const markerHoverForStatusbar = hoverParts.length === 1 ? hoverParts[0] : hoverParts.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))[0];
  87. this.renderMarkerStatusbar(context, markerHoverForStatusbar, disposables);
  88. return disposables;
  89. }
  90. renderMarkerHover(markerHover, disposables) {
  91. const hoverElement = $('div.hover-row');
  92. const markerElement = dom.append(hoverElement, $('div.marker.hover-contents'));
  93. const { source, message, code, relatedInformation } = markerHover.marker;
  94. this._editor.applyFontInfo(markerElement);
  95. const messageElement = dom.append(markerElement, $('span'));
  96. messageElement.style.whiteSpace = 'pre-wrap';
  97. messageElement.innerText = message;
  98. if (source || code) {
  99. // Code has link
  100. if (code && typeof code !== 'string') {
  101. const sourceAndCodeElement = $('span');
  102. if (source) {
  103. const sourceElement = dom.append(sourceAndCodeElement, $('span'));
  104. sourceElement.innerText = source;
  105. }
  106. const codeLink = dom.append(sourceAndCodeElement, $('a.code-link'));
  107. codeLink.setAttribute('href', code.target.toString());
  108. disposables.add(dom.addDisposableListener(codeLink, 'click', (e) => {
  109. this._openerService.open(code.target, { allowCommands: true });
  110. e.preventDefault();
  111. e.stopPropagation();
  112. }));
  113. const codeElement = dom.append(codeLink, $('span'));
  114. codeElement.innerText = code.value;
  115. const detailsElement = dom.append(markerElement, sourceAndCodeElement);
  116. detailsElement.style.opacity = '0.6';
  117. detailsElement.style.paddingLeft = '6px';
  118. }
  119. else {
  120. const detailsElement = dom.append(markerElement, $('span'));
  121. detailsElement.style.opacity = '0.6';
  122. detailsElement.style.paddingLeft = '6px';
  123. detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`;
  124. }
  125. }
  126. if (isNonEmptyArray(relatedInformation)) {
  127. for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
  128. const relatedInfoContainer = dom.append(markerElement, $('div'));
  129. relatedInfoContainer.style.marginTop = '8px';
  130. const a = dom.append(relatedInfoContainer, $('a'));
  131. a.innerText = `${basename(resource)}(${startLineNumber}, ${startColumn}): `;
  132. a.style.cursor = 'pointer';
  133. disposables.add(dom.addDisposableListener(a, 'click', (e) => {
  134. e.stopPropagation();
  135. e.preventDefault();
  136. if (this._openerService) {
  137. this._openerService.open(resource, {
  138. fromUserGesture: true,
  139. editorOptions: { selection: { startLineNumber, startColumn } }
  140. }).catch(onUnexpectedError);
  141. }
  142. }));
  143. const messageElement = dom.append(relatedInfoContainer, $('span'));
  144. messageElement.innerText = message;
  145. this._editor.applyFontInfo(messageElement);
  146. }
  147. }
  148. return hoverElement;
  149. }
  150. renderMarkerStatusbar(context, markerHover, disposables) {
  151. if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
  152. context.statusBar.addAction({
  153. label: nls.localize('view problem', "View Problem"),
  154. commandId: NextMarkerAction.ID,
  155. run: () => {
  156. var _a;
  157. context.hide();
  158. (_a = MarkerController.get(this._editor)) === null || _a === void 0 ? void 0 : _a.showAtMarker(markerHover.marker);
  159. this._editor.focus();
  160. }
  161. });
  162. }
  163. if (!this._editor.getOption(83 /* EditorOption.readOnly */)) {
  164. const quickfixPlaceholderElement = context.statusBar.append($('div'));
  165. if (this.recentMarkerCodeActionsInfo) {
  166. if (IMarkerData.makeKey(this.recentMarkerCodeActionsInfo.marker) === IMarkerData.makeKey(markerHover.marker)) {
  167. if (!this.recentMarkerCodeActionsInfo.hasCodeActions) {
  168. quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
  169. }
  170. }
  171. else {
  172. this.recentMarkerCodeActionsInfo = undefined;
  173. }
  174. }
  175. const updatePlaceholderDisposable = this.recentMarkerCodeActionsInfo && !this.recentMarkerCodeActionsInfo.hasCodeActions ? Disposable.None : disposables.add(disposableTimeout(() => quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."), 200));
  176. if (!quickfixPlaceholderElement.textContent) {
  177. // Have some content in here to avoid flickering
  178. quickfixPlaceholderElement.textContent = String.fromCharCode(0xA0); // &nbsp;
  179. }
  180. const codeActionsPromise = this.getCodeActions(markerHover.marker);
  181. disposables.add(toDisposable(() => codeActionsPromise.cancel()));
  182. codeActionsPromise.then(actions => {
  183. updatePlaceholderDisposable.dispose();
  184. this.recentMarkerCodeActionsInfo = { marker: markerHover.marker, hasCodeActions: actions.validActions.length > 0 };
  185. if (!this.recentMarkerCodeActionsInfo.hasCodeActions) {
  186. actions.dispose();
  187. quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
  188. return;
  189. }
  190. quickfixPlaceholderElement.style.display = 'none';
  191. let showing = false;
  192. disposables.add(toDisposable(() => {
  193. if (!showing) {
  194. actions.dispose();
  195. }
  196. }));
  197. context.statusBar.addAction({
  198. label: nls.localize('quick fixes', "Quick Fix..."),
  199. commandId: QuickFixAction.Id,
  200. run: (target) => {
  201. showing = true;
  202. const controller = QuickFixController.get(this._editor);
  203. const elementPosition = dom.getDomNodePagePosition(target);
  204. // Hide the hover pre-emptively, otherwise the editor can close the code actions
  205. // context menu as well when using keyboard navigation
  206. context.hide();
  207. controller === null || controller === void 0 ? void 0 : controller.showCodeActions(markerCodeActionTrigger, actions, {
  208. x: elementPosition.left + 6,
  209. y: elementPosition.top + elementPosition.height + 6,
  210. width: elementPosition.width,
  211. height: elementPosition.height
  212. });
  213. }
  214. });
  215. }, onUnexpectedError);
  216. }
  217. }
  218. getCodeActions(marker) {
  219. return createCancelablePromise(cancellationToken => {
  220. return getCodeActions(this._languageFeaturesService.codeActionProvider, this._editor.getModel(), new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), markerCodeActionTrigger, Progress.None, cancellationToken);
  221. });
  222. }
  223. };
  224. MarkerHoverParticipant = __decorate([
  225. __param(1, IMarkerDecorationsService),
  226. __param(2, IOpenerService),
  227. __param(3, ILanguageFeaturesService)
  228. ], MarkerHoverParticipant);
  229. export { MarkerHoverParticipant };
  230. registerThemingParticipant((theme, collector) => {
  231. const linkFg = theme.getColor(textLinkForeground);
  232. if (linkFg) {
  233. collector.addRule(`.monaco-hover .hover-contents a.code-link span { color: ${linkFg}; }`);
  234. }
  235. const activeLinkFg = theme.getColor(textLinkActiveForeground);
  236. if (activeLinkFg) {
  237. collector.addRule(`.monaco-hover .hover-contents a.code-link span:hover { color: ${activeLinkFg}; }`);
  238. }
  239. });