alm
Version:
The best IDE for TypeScript
284 lines (283 loc) • 12.2 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Renders tested stuff in monaco
*/
var clientTestResultsCache_1 = require("../../clientTestResultsCache");
var events = require("../../../common/events");
var utils = require("../../../common/utils");
var types = require("../../../common/types");
var json = require("../../../common/json");
var typestyle = require("typestyle");
var styles = require("../../styles/styles");
var icon_1 = require("../../components/icon");
var React = require("react");
var ReactDOM = require("react-dom");
var Position = monaco.Position;
var keyForMonacoDifferentiation = "alm_tested";
var lineSeperator = '\n———————————————\n';
var TestedMonacoStyles;
(function (TestedMonacoStyles) {
var overlayCommon = {
padding: '0px 10px',
whiteSpace: 'pre',
pointerEvents: 'none',
/**
* This is to match the line height for a line in monaco
* Inspected a line in monaco to figure this out
* On mac it was 24px
* On windows it was 22px.
* Going with the small value globally instead of trying to figure it out
*/
lineHeight: '22px',
};
TestedMonacoStyles.logOverlayClassName = typestyle.style(overlayCommon, {
color: styles.monokaiTextColor,
});
TestedMonacoStyles.errorStackOverlayClassName = typestyle.style(overlayCommon, {
color: styles.errorColor,
});
})(TestedMonacoStyles || (TestedMonacoStyles = {}));
function setup(editor) {
// if (editor) return { dispose: () => null }; // DEBUG : while the feature isn't complete used to disable it
var hadSomeTestsResults = false;
var deltaLogWidgets = new DeltaList({
getId: function (log) {
return keyForMonacoDifferentiation + " - " + JSON.stringify(log);
},
onAdd: function (log) {
var argsStringifiedAndJoined = log.args.map(function (a) { return json.stringify(a).trim(); })
.join(lineSeperator);
var nodeRendered = React.createElement("div", { className: TestedMonacoStyles.logOverlayClassName }, argsStringifiedAndJoined);
var node = document.createElement('div');
ReactDOM.render(nodeRendered, node);
var widgetDispose = MonacoInlineWidget.add({
editor: editor,
frameColor: styles.monokaiTextColor,
domNode: node,
position: log.testLogPosition.lastPositionInFile,
heightInLines: argsStringifiedAndJoined.split('\n').length + 1,
});
return widgetDispose;
},
onRemove: function (log, state) { return state.dispose(); },
});
var deltaTestResultsWidgets = new DeltaList({
getId: function (result) {
return keyForMonacoDifferentiation + " - " + JSON.stringify(result);
},
onAdd: function (result) {
var disposible = new events.CompositeDisposible();
/**
* Show pass fail in the editor.
* Would prefer in gutter but monaco doesn't allow multiple gutters.
* So adding them inline as circles.
* They move to the gutter based on our column setting ;)
*/
var dotRendered = React.createElement("div", { className: "hint--right " + (result.status === types.TestStatus.Success ? "hint--success"
: result.status === types.TestStatus.Fail ? "hint--error"
: "hint--info"), style: {
cursor: 'pointer',
padding: '0px 5px',
color: result.status === types.TestStatus.Success ? styles.successColor
: result.status === types.TestStatus.Fail ? styles.errorColor
: styles.highlightColor
}, "data-hint": result.status === types.TestStatus.Success ? "Test Success"
: result.status === types.TestStatus.Fail ? "Test Fail: " + result.error.message
: "Test Skipped" },
React.createElement(icon_1.Icon, { name: styles.icons.tested }));
var dotNode = document.createElement('div');
ReactDOM.render(dotRendered, dotNode);
var widget = {
allowEditorOverflow: false,
getId: function () { return keyForMonacoDifferentiation + " - dot - " + JSON.stringify(result); },
getDomNode: function () { return dotNode; },
getPosition: function () {
return {
position: {
lineNumber: result.testLogPosition.lastPositionInFile.line + 1,
column: /** Show in start of line to keep it easier to scan with eye */ 1
},
preference: [
monaco.editor.ContentWidgetPositionPreference.EXACT
]
};
}
};
editor.addContentWidget(widget);
disposible.add({
dispose: function () { return editor.removeContentWidget(widget); }
});
/**
* Show stacks for error ones
*/
if (!result.error) {
return disposible;
}
var trailingStackStringifiedAndJoined = result.error.stack
.slice(1)
.map(function (a) { return a.filePath + ":" + (a.position.line + 1) + ":" + (a.position.ch + 1); })
.join(lineSeperator);
var detailsStringifiedAndJoined = result.error.message +
(trailingStackStringifiedAndJoined
? "" + lineSeperator + trailingStackStringifiedAndJoined
: '');
var nodeRendered = React.createElement("div", { className: TestedMonacoStyles.errorStackOverlayClassName }, detailsStringifiedAndJoined);
var node = document.createElement('div');
ReactDOM.render(nodeRendered, node);
var widgetDispose = MonacoInlineWidget.add({
editor: editor,
frameColor: styles.errorColor,
domNode: node,
position: result.error.testLogPosition.lastPositionInFile,
heightInLines: detailsStringifiedAndJoined.split('\n').length + 1,
});
disposible.add(widgetDispose);
return disposible;
},
onRemove: function (result, state) { return state.dispose(); },
});
var performLogRefresh = utils.debounce(function () {
var filePath = editor.filePath;
var model = editor.getModel();
var allResults = clientTestResultsCache_1.testResultsCache.getResults();
var thisModule = allResults[filePath];
if (!thisModule) {
if (hadSomeTestsResults) {
deltaLogWidgets.delta([]);
deltaTestResultsWidgets.delta([]);
}
hadSomeTestsResults = false;
return;
}
hadSomeTestsResults = true;
/**
* Update logs for this file
* For those found update them
* For those not found delete them
* For those new add them.
*
* show logs in this file inline
* show logs in external files still inline
*/
deltaLogWidgets.delta(thisModule.logs);
/** Also show the test results */
deltaTestResultsWidgets.delta(thisModule.testResults);
// console.log(thisModule.logs); // DEBUG
}, 500);
// Perform an initial lint
performLogRefresh();
var disposible = new events.CompositeDisposible();
// Subscribe for future updates
disposible.add(clientTestResultsCache_1.testResultsCache.testResultsDelta.on(performLogRefresh));
return disposible;
}
exports.setup = setup;
/**
* Imagine a data structure that takes `T[]`
* and given `getId(): T`
*
* Calls these on delta for `T[]`
*
* onAdd => do stuff and give me some state I will call for onRemove
* onRemove => do stuff
*/
var DeltaList = /** @class */ (function () {
function DeltaList(config) {
this.config = config;
this.map = Object.create(null);
}
DeltaList.prototype.delta = function (items) {
var _this = this;
/** for quick lookup */
var quickNewItemLookup = utils.createMap(items.map(this.config.getId));
/** New dict */
var newDict = this.map;
newDict = Object.create(null);
/** old dict */
var oldDict = this.map;
items.forEach(function (item) {
var id = _this.config.getId(item);
/** Added? */
if (!oldDict[id]) {
var state = _this.config.onAdd(item);
newDict[id] = {
item: item,
state: state
};
}
else {
newDict[id] = oldDict[id];
}
});
/** Removed? */
Object.keys(oldDict).forEach(function (id) {
if (!quickNewItemLookup[id]) {
_this.config.onRemove(oldDict[id].item, oldDict[id].state);
}
});
this.map = newDict;
};
return DeltaList;
}());
var MonacoInlineWidget;
(function (MonacoInlineWidget) {
;
var ZoneWidget = monacoRequire('vs/editor/contrib/zoneWidget/browser/zoneWidget').ZoneWidget;
/** For reference see `gotoError.ts` in monaco source code */
var MyMarkerWidget = /** @class */ (function (_super) {
__extends(MyMarkerWidget, _super);
function MyMarkerWidget(config) {
var _this = _super.call(this, config.editor, {
frameColor: config.frameColor,
}) || this;
_this.config = config;
_this.create();
var position = new Position(config.position.line + 1, config.position.ch + 1);
_this.show(position, config.heightInLines);
return _this;
}
MyMarkerWidget.prototype._fillContainer = function (container) {
this._parentContainer = container;
this._parentContainer.tabIndex = 0;
this._parentContainer.setAttribute('role', 'tooltip');
this._parentContainer.appendChild(this.config.domNode);
/** Because sometimes monaco is leaving text hanging around */
this._parentContainer.style.backgroundColor = styles.monokaiBackgroundColor;
};
MyMarkerWidget.prototype.dispose = function () {
_super.prototype.dispose.call(this);
};
return MyMarkerWidget;
}(ZoneWidget));
function add(config) {
var editor = config.editor;
var position = editor.getPosition();
var revealPosition = editor.revealPosition;
var revealLine = editor.revealLine;
editor.revealPosition = function () { return null; };
editor.revealLine = function () { return null; };
/** Add */
var widget = new MyMarkerWidget(config);
/**
* Our inline log widgets jump the scroll position. So we needed disable and restore these functions :-/
* Also some of these are called asyncly for some reason.
* See code in ZoneWidget._showImpl
*/
editor.setPosition(position);
editor.revealPosition = revealPosition;
editor.revealLine = revealLine;
return widget;
}
MonacoInlineWidget.add = add;
})(MonacoInlineWidget || (MonacoInlineWidget = {}));