94cdc356a8cd328ba6c30f30aa31bc01159b5a8d5ab8ee645c950779426197f9e634a77d4295445ebe3a993588954482e36d99375bf09f4350b357724fa8a9 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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 { Gesture } from '../../../../base/browser/touch.js';
  16. import { Codicon } from '../../../../base/common/codicons.js';
  17. import { Emitter } from '../../../../base/common/event.js';
  18. import { Disposable } from '../../../../base/common/lifecycle.js';
  19. import './lightBulbWidget.css';
  20. import { computeIndentLevel } from '../../../common/model/utils.js';
  21. import * as nls from '../../../../nls.js';
  22. import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
  23. import { editorBackground, editorLightBulbAutoFixForeground, editorLightBulbForeground } from '../../../../platform/theme/common/colorRegistry.js';
  24. import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
  25. var LightBulbState;
  26. (function (LightBulbState) {
  27. LightBulbState.Hidden = { type: 0 /* Type.Hidden */ };
  28. class Showing {
  29. constructor(actions, trigger, editorPosition, widgetPosition) {
  30. this.actions = actions;
  31. this.trigger = trigger;
  32. this.editorPosition = editorPosition;
  33. this.widgetPosition = widgetPosition;
  34. this.type = 1 /* Type.Showing */;
  35. }
  36. }
  37. LightBulbState.Showing = Showing;
  38. })(LightBulbState || (LightBulbState = {}));
  39. let LightBulbWidget = class LightBulbWidget extends Disposable {
  40. constructor(_editor, _quickFixActionId, _preferredFixActionId, _keybindingService) {
  41. super();
  42. this._editor = _editor;
  43. this._quickFixActionId = _quickFixActionId;
  44. this._preferredFixActionId = _preferredFixActionId;
  45. this._keybindingService = _keybindingService;
  46. this._onClick = this._register(new Emitter());
  47. this.onClick = this._onClick.event;
  48. this._state = LightBulbState.Hidden;
  49. this._domNode = document.createElement('div');
  50. this._domNode.className = Codicon.lightBulb.classNames;
  51. this._editor.addContentWidget(this);
  52. this._register(this._editor.onDidChangeModelContent(_ => {
  53. // cancel when the line in question has been removed
  54. const editorModel = this._editor.getModel();
  55. if (this.state.type !== 1 /* LightBulbState.Type.Showing */ || !editorModel || this.state.editorPosition.lineNumber >= editorModel.getLineCount()) {
  56. this.hide();
  57. }
  58. }));
  59. Gesture.ignoreTarget(this._domNode);
  60. this._register(dom.addStandardDisposableGenericMouseDownListener(this._domNode, e => {
  61. if (this.state.type !== 1 /* LightBulbState.Type.Showing */) {
  62. return;
  63. }
  64. // Make sure that focus / cursor location is not lost when clicking widget icon
  65. this._editor.focus();
  66. e.preventDefault();
  67. // a bit of extra work to make sure the menu
  68. // doesn't cover the line-text
  69. const { top, height } = dom.getDomNodePagePosition(this._domNode);
  70. const lineHeight = this._editor.getOption(61 /* EditorOption.lineHeight */);
  71. let pad = Math.floor(lineHeight / 3);
  72. if (this.state.widgetPosition.position !== null && this.state.widgetPosition.position.lineNumber < this.state.editorPosition.lineNumber) {
  73. pad += lineHeight;
  74. }
  75. this._onClick.fire({
  76. x: e.posx,
  77. y: top + height + pad,
  78. actions: this.state.actions,
  79. trigger: this.state.trigger,
  80. });
  81. }));
  82. this._register(dom.addDisposableListener(this._domNode, 'mouseenter', (e) => {
  83. if ((e.buttons & 1) !== 1) {
  84. return;
  85. }
  86. // mouse enters lightbulb while the primary/left button
  87. // is being pressed -> hide the lightbulb
  88. this.hide();
  89. }));
  90. this._register(this._editor.onDidChangeConfiguration(e => {
  91. // hide when told to do so
  92. if (e.hasChanged(59 /* EditorOption.lightbulb */) && !this._editor.getOption(59 /* EditorOption.lightbulb */).enabled) {
  93. this.hide();
  94. }
  95. }));
  96. this._updateLightBulbTitleAndIcon();
  97. this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitleAndIcon, this));
  98. }
  99. dispose() {
  100. super.dispose();
  101. this._editor.removeContentWidget(this);
  102. }
  103. getId() {
  104. return 'LightBulbWidget';
  105. }
  106. getDomNode() {
  107. return this._domNode;
  108. }
  109. getPosition() {
  110. return this._state.type === 1 /* LightBulbState.Type.Showing */ ? this._state.widgetPosition : null;
  111. }
  112. update(actions, trigger, atPosition) {
  113. if (actions.validActions.length <= 0) {
  114. return this.hide();
  115. }
  116. const options = this._editor.getOptions();
  117. if (!options.get(59 /* EditorOption.lightbulb */).enabled) {
  118. return this.hide();
  119. }
  120. const model = this._editor.getModel();
  121. if (!model) {
  122. return this.hide();
  123. }
  124. const { lineNumber, column } = model.validatePosition(atPosition);
  125. const tabSize = model.getOptions().tabSize;
  126. const fontInfo = options.get(46 /* EditorOption.fontInfo */);
  127. const lineContent = model.getLineContent(lineNumber);
  128. const indent = computeIndentLevel(lineContent, tabSize);
  129. const lineHasSpace = fontInfo.spaceWidth * indent > 22;
  130. const isFolded = (lineNumber) => {
  131. return lineNumber > 2 && this._editor.getTopForLineNumber(lineNumber) === this._editor.getTopForLineNumber(lineNumber - 1);
  132. };
  133. let effectiveLineNumber = lineNumber;
  134. if (!lineHasSpace) {
  135. if (lineNumber > 1 && !isFolded(lineNumber - 1)) {
  136. effectiveLineNumber -= 1;
  137. }
  138. else if (!isFolded(lineNumber + 1)) {
  139. effectiveLineNumber += 1;
  140. }
  141. else if (column * fontInfo.spaceWidth < 22) {
  142. // cannot show lightbulb above/below and showing
  143. // it inline would overlay the cursor...
  144. return this.hide();
  145. }
  146. }
  147. this.state = new LightBulbState.Showing(actions, trigger, atPosition, {
  148. position: { lineNumber: effectiveLineNumber, column: 1 },
  149. preference: LightBulbWidget._posPref
  150. });
  151. this._editor.layoutContentWidget(this);
  152. }
  153. hide() {
  154. this.state = LightBulbState.Hidden;
  155. this._editor.layoutContentWidget(this);
  156. }
  157. get state() { return this._state; }
  158. set state(value) {
  159. this._state = value;
  160. this._updateLightBulbTitleAndIcon();
  161. }
  162. _updateLightBulbTitleAndIcon() {
  163. if (this.state.type === 1 /* LightBulbState.Type.Showing */ && this.state.actions.hasAutoFix) {
  164. // update icon
  165. this._domNode.classList.remove(...Codicon.lightBulb.classNamesArray);
  166. this._domNode.classList.add(...Codicon.lightbulbAutofix.classNamesArray);
  167. const preferredKb = this._keybindingService.lookupKeybinding(this._preferredFixActionId);
  168. if (preferredKb) {
  169. this.title = nls.localize('preferredcodeActionWithKb', "Show Code Actions. Preferred Quick Fix Available ({0})", preferredKb.getLabel());
  170. return;
  171. }
  172. }
  173. // update icon
  174. this._domNode.classList.remove(...Codicon.lightbulbAutofix.classNamesArray);
  175. this._domNode.classList.add(...Codicon.lightBulb.classNamesArray);
  176. const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId);
  177. if (kb) {
  178. this.title = nls.localize('codeActionWithKb', "Show Code Actions ({0})", kb.getLabel());
  179. }
  180. else {
  181. this.title = nls.localize('codeAction', "Show Code Actions");
  182. }
  183. }
  184. set title(value) {
  185. this._domNode.title = value;
  186. }
  187. };
  188. LightBulbWidget._posPref = [0 /* ContentWidgetPositionPreference.EXACT */];
  189. LightBulbWidget = __decorate([
  190. __param(3, IKeybindingService)
  191. ], LightBulbWidget);
  192. export { LightBulbWidget };
  193. registerThemingParticipant((theme, collector) => {
  194. var _a;
  195. const editorBackgroundColor = (_a = theme.getColor(editorBackground)) === null || _a === void 0 ? void 0 : _a.transparent(0.7);
  196. // Lightbulb Icon
  197. const editorLightBulbForegroundColor = theme.getColor(editorLightBulbForeground);
  198. if (editorLightBulbForegroundColor) {
  199. collector.addRule(`
  200. .monaco-editor .contentWidgets ${Codicon.lightBulb.cssSelector} {
  201. color: ${editorLightBulbForegroundColor};
  202. background-color: ${editorBackgroundColor};
  203. }`);
  204. }
  205. // Lightbulb Auto Fix Icon
  206. const editorLightBulbAutoFixForegroundColor = theme.getColor(editorLightBulbAutoFixForeground);
  207. if (editorLightBulbAutoFixForegroundColor) {
  208. collector.addRule(`
  209. .monaco-editor .contentWidgets ${Codicon.lightbulbAutofix.cssSelector} {
  210. color: ${editorLightBulbAutoFixForegroundColor};
  211. background-color: ${editorBackgroundColor};
  212. }`);
  213. }
  214. });