inlineCompletionsHoverParticipant.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 { MarkdownString } from '../../../base/common/htmlContent.js';
  16. import { DisposableStore } from '../../../base/common/lifecycle.js';
  17. import { MarkdownRenderer } from '../../browser/core/markdownRenderer.js';
  18. import { Range } from '../../common/core/range.js';
  19. import { IModeService } from '../../common/services/modeService.js';
  20. import { HoverForeignElementAnchor } from '../hover/hoverTypes.js';
  21. import { commitInlineSuggestionAction, GhostTextController, ShowNextInlineSuggestionAction, ShowPreviousInlineSuggestionAction } from './ghostTextController.js';
  22. import * as nls from '../../../nls.js';
  23. import { IAccessibilityService } from '../../../platform/accessibility/common/accessibility.js';
  24. import { IMenuService, MenuId, MenuItemAction } from '../../../platform/actions/common/actions.js';
  25. import { ICommandService } from '../../../platform/commands/common/commands.js';
  26. import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
  27. import { IOpenerService } from '../../../platform/opener/common/opener.js';
  28. export class InlineCompletionsHover {
  29. constructor(owner, range, controller) {
  30. this.owner = owner;
  31. this.range = range;
  32. this.controller = controller;
  33. }
  34. isValidForHoverAnchor(anchor) {
  35. return (anchor.type === 1 /* Range */
  36. && this.range.startColumn <= anchor.range.startColumn
  37. && this.range.endColumn >= anchor.range.endColumn);
  38. }
  39. hasMultipleSuggestions() {
  40. return this.controller.hasMultipleInlineCompletions();
  41. }
  42. }
  43. let InlineCompletionsHoverParticipant = class InlineCompletionsHoverParticipant {
  44. constructor(_editor, _hover, _commandService, _menuService, _contextKeyService, _modeService, _openerService, accessibilityService) {
  45. this._editor = _editor;
  46. this._hover = _hover;
  47. this._commandService = _commandService;
  48. this._menuService = _menuService;
  49. this._contextKeyService = _contextKeyService;
  50. this._modeService = _modeService;
  51. this._openerService = _openerService;
  52. this.accessibilityService = accessibilityService;
  53. }
  54. suggestHoverAnchor(mouseEvent) {
  55. const controller = GhostTextController.get(this._editor);
  56. if (!controller) {
  57. return null;
  58. }
  59. if (mouseEvent.target.type === 8 /* CONTENT_VIEW_ZONE */) {
  60. // handle the case where the mouse is over the view zone
  61. const viewZoneData = mouseEvent.target.detail;
  62. if (controller.shouldShowHoverAtViewZone(viewZoneData.viewZoneId)) {
  63. return new HoverForeignElementAnchor(1000, this, Range.fromPositions(viewZoneData.positionBefore || viewZoneData.position, viewZoneData.positionBefore || viewZoneData.position));
  64. }
  65. }
  66. if (mouseEvent.target.type === 7 /* CONTENT_EMPTY */ && mouseEvent.target.range) {
  67. // handle the case where the mouse is over the empty portion of a line following ghost text
  68. if (controller.shouldShowHoverAt(mouseEvent.target.range)) {
  69. return new HoverForeignElementAnchor(1000, this, mouseEvent.target.range);
  70. }
  71. }
  72. if (mouseEvent.target.type === 6 /* CONTENT_TEXT */ && mouseEvent.target.range && mouseEvent.target.detail) {
  73. // handle the case where the mouse is directly over ghost text
  74. const mightBeForeignElement = mouseEvent.target.detail.mightBeForeignElement;
  75. if (mightBeForeignElement && controller.shouldShowHoverAt(mouseEvent.target.range)) {
  76. return new HoverForeignElementAnchor(1000, this, mouseEvent.target.range);
  77. }
  78. }
  79. return null;
  80. }
  81. computeSync(anchor, lineDecorations) {
  82. const controller = GhostTextController.get(this._editor);
  83. if (controller && controller.shouldShowHoverAt(anchor.range)) {
  84. return [new InlineCompletionsHover(this, anchor.range, controller)];
  85. }
  86. return [];
  87. }
  88. renderHoverParts(hoverParts, fragment, statusBar) {
  89. const disposableStore = new DisposableStore();
  90. const part = hoverParts[0];
  91. if (this.accessibilityService.isScreenReaderOptimized()) {
  92. this.renderScreenReaderText(part, fragment, disposableStore);
  93. }
  94. const menu = disposableStore.add(this._menuService.createMenu(MenuId.InlineCompletionsActions, this._contextKeyService));
  95. const previousAction = statusBar.addAction({
  96. label: nls.localize('showNextInlineSuggestion', "Next"),
  97. commandId: ShowNextInlineSuggestionAction.ID,
  98. run: () => this._commandService.executeCommand(ShowNextInlineSuggestionAction.ID)
  99. });
  100. const nextAction = statusBar.addAction({
  101. label: nls.localize('showPreviousInlineSuggestion', "Previous"),
  102. commandId: ShowPreviousInlineSuggestionAction.ID,
  103. run: () => this._commandService.executeCommand(ShowPreviousInlineSuggestionAction.ID)
  104. });
  105. statusBar.addAction({
  106. label: nls.localize('acceptInlineSuggestion', "Accept"),
  107. commandId: commitInlineSuggestionAction.id,
  108. run: () => this._commandService.executeCommand(commitInlineSuggestionAction.id)
  109. });
  110. const actions = [previousAction, nextAction];
  111. for (const action of actions) {
  112. action.setEnabled(false);
  113. }
  114. part.hasMultipleSuggestions().then(hasMore => {
  115. for (const action of actions) {
  116. action.setEnabled(hasMore);
  117. }
  118. });
  119. for (const [_, group] of menu.getActions()) {
  120. for (const action of group) {
  121. if (action instanceof MenuItemAction) {
  122. statusBar.addAction({
  123. label: action.label,
  124. commandId: action.item.id,
  125. run: () => this._commandService.executeCommand(action.item.id)
  126. });
  127. }
  128. }
  129. }
  130. return disposableStore;
  131. }
  132. renderScreenReaderText(part, fragment, disposableStore) {
  133. var _a, _b;
  134. const $ = dom.$;
  135. const markdownHoverElement = $('div.hover-row.markdown-hover');
  136. const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
  137. const renderer = disposableStore.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService));
  138. const render = (code) => {
  139. disposableStore.add(renderer.onDidRenderAsync(() => {
  140. hoverContentsElement.className = 'hover-contents code-hover-contents';
  141. this._hover.onContentsChanged();
  142. }));
  143. const inlineSuggestionAvailable = nls.localize('inlineSuggestionFollows', "Suggestion:");
  144. const renderedContents = disposableStore.add(renderer.render(new MarkdownString().appendText(inlineSuggestionAvailable).appendCodeblock('text', code)));
  145. hoverContentsElement.replaceChildren(renderedContents.element);
  146. };
  147. const ghostText = (_b = (_a = part.controller.activeModel) === null || _a === void 0 ? void 0 : _a.inlineCompletionsModel) === null || _b === void 0 ? void 0 : _b.ghostText;
  148. if (ghostText) {
  149. const lineText = this._editor.getModel().getLineContent(ghostText.lineNumber);
  150. render(ghostText.renderForScreenReader(lineText));
  151. }
  152. fragment.appendChild(markdownHoverElement);
  153. }
  154. };
  155. InlineCompletionsHoverParticipant = __decorate([
  156. __param(2, ICommandService),
  157. __param(3, IMenuService),
  158. __param(4, IContextKeyService),
  159. __param(5, IModeService),
  160. __param(6, IOpenerService),
  161. __param(7, IAccessibilityService)
  162. ], InlineCompletionsHoverParticipant);
  163. export { InlineCompletionsHoverParticipant };