monaco-editor
Version:
A browser based code editor
185 lines (184 loc) • 8.39 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from '../../../base/browser/dom.js';
import { GlobalMouseMoveMonitor, standardMouseMoveMerger } from '../../../base/browser/globalMouseMoveMonitor.js';
import { CancellationTokenSource } from '../../../base/common/cancellation.js';
import { Emitter } from '../../../base/common/event.js';
import { dispose } from '../../../base/common/lifecycle.js';
import './lightBulbWidget.css';
import { TextModel } from '../../common/model/textModel.js';
import { CodeActionKind } from './codeActionTrigger.js';
var LightBulbWidget = /** @class */ (function () {
function LightBulbWidget(editor) {
var _this = this;
this._disposables = [];
this._onClick = new Emitter();
this.onClick = this._onClick.event;
this._futureFixes = new CancellationTokenSource();
this._domNode = document.createElement('div');
this._domNode.className = 'lightbulb-glyph';
this._editor = editor;
this._editor.addContentWidget(this);
this._disposables.push(this._editor.onDidChangeModel(function (_) { return _this._futureFixes.cancel(); }));
this._disposables.push(this._editor.onDidChangeModelLanguage(function (_) { return _this._futureFixes.cancel(); }));
this._disposables.push(this._editor.onDidChangeModelContent(function (_) {
// cancel when the line in question has been removed
var editorModel = _this._editor.getModel();
if (!_this.model || !_this.model.position || !editorModel || _this.model.position.lineNumber >= editorModel.getLineCount()) {
_this._futureFixes.cancel();
}
}));
this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', function (e) {
// Make sure that focus / cursor location is not lost when clicking widget icon
_this._editor.focus();
// a bit of extra work to make sure the menu
// doesn't cover the line-text
var _a = dom.getDomNodePagePosition(_this._domNode), top = _a.top, height = _a.height;
var lineHeight = _this._editor.getConfiguration().lineHeight;
var pad = Math.floor(lineHeight / 3);
if (_this._position && _this._model && _this._model.position && _this._position.position !== null && _this._position.position.lineNumber < _this._model.position.lineNumber) {
pad += lineHeight;
}
_this._onClick.fire({
x: e.posx,
y: top + height + pad
});
}));
this._disposables.push(dom.addDisposableListener(this._domNode, 'mouseenter', function (e) {
if ((e.buttons & 1) !== 1) {
return;
}
// mouse enters lightbulb while the primary/left button
// is being pressed -> hide the lightbulb and block future
// showings until mouse is released
_this.hide();
var monitor = new GlobalMouseMoveMonitor();
monitor.startMonitoring(standardMouseMoveMerger, function () { }, function () {
monitor.dispose();
});
}));
this._disposables.push(this._editor.onDidChangeConfiguration(function (e) {
// hide when told to do so
if (e.contribInfo && !_this._editor.getConfiguration().contribInfo.lightbulbEnabled) {
_this.hide();
}
}));
}
LightBulbWidget.prototype.dispose = function () {
dispose(this._disposables);
this._editor.removeContentWidget(this);
};
LightBulbWidget.prototype.getId = function () {
return 'LightBulbWidget';
};
LightBulbWidget.prototype.getDomNode = function () {
return this._domNode;
};
LightBulbWidget.prototype.getPosition = function () {
return this._position;
};
Object.defineProperty(LightBulbWidget.prototype, "model", {
get: function () {
return this._model;
},
set: function (value) {
var _this = this;
if (!value || this._position && (!value.position || this._position.position && this._position.position.lineNumber !== value.position.lineNumber)) {
// hide when getting a 'hide'-request or when currently
// showing on another line
this.hide();
}
else if (this._futureFixes) {
// cancel pending show request in any case
this._futureFixes.cancel();
}
this._futureFixes = new CancellationTokenSource();
var token = this._futureFixes.token;
this._model = value;
if (!this._model || !this._model.actions) {
return;
}
var selection = this._model.rangeOrSelection;
this._model.actions.then(function (fixes) {
if (!token.isCancellationRequested && fixes && fixes.length > 0) {
if (!selection || selection.isEmpty() && fixes.every(function (fix) { return !!(fix.kind && CodeActionKind.Refactor.contains(fix.kind)); })) {
_this.hide();
}
else {
_this._show();
}
}
else {
_this.hide();
}
}).catch(function () {
_this.hide();
});
},
enumerable: true,
configurable: true
});
Object.defineProperty(LightBulbWidget.prototype, "title", {
get: function () {
return this._domNode.title;
},
set: function (value) {
this._domNode.title = value;
},
enumerable: true,
configurable: true
});
LightBulbWidget.prototype._show = function () {
var _this = this;
var config = this._editor.getConfiguration();
if (!config.contribInfo.lightbulbEnabled) {
return;
}
if (!this._model || !this._model.position) {
return;
}
var _a = this._model.position, lineNumber = _a.lineNumber, column = _a.column;
var model = this._editor.getModel();
if (!model) {
return;
}
var tabSize = model.getOptions().tabSize;
var lineContent = model.getLineContent(lineNumber);
var indent = TextModel.computeIndentLevel(lineContent, tabSize);
var lineHasSpace = config.fontInfo.spaceWidth * indent > 22;
var isFolded = function (lineNumber) {
return lineNumber > 2 && _this._editor.getTopForLineNumber(lineNumber) === _this._editor.getTopForLineNumber(lineNumber - 1);
};
var effectiveLineNumber = lineNumber;
if (!lineHasSpace) {
if (lineNumber > 1 && !isFolded(lineNumber - 1)) {
effectiveLineNumber -= 1;
}
else if (!isFolded(lineNumber + 1)) {
effectiveLineNumber += 1;
}
else if (column * config.fontInfo.spaceWidth < 22) {
// cannot show lightbulb above/below and showing
// it inline would overlay the cursor...
this.hide();
return;
}
}
this._position = {
position: { lineNumber: effectiveLineNumber, column: 1 },
preference: LightBulbWidget._posPref
};
this._editor.layoutContentWidget(this);
};
LightBulbWidget.prototype.hide = function () {
this._position = null;
this._model = null;
this._futureFixes.cancel();
this._editor.layoutContentWidget(this);
};
LightBulbWidget._posPref = [0 /* EXACT */];
return LightBulbWidget;
}());
export { LightBulbWidget };