UNPKG

alm

Version:

The best IDE for TypeScript

284 lines (283 loc) 12.2 kB
"use strict"; 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 = {}));