UNPKG

alm

Version:

The best IDE for TypeScript

329 lines (328 loc) 14.8 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 }); var ui = require("../../ui"); var csx = require("../../base/csx"); var React = require("react"); var socketClient_1 = require("../../../socket/socketClient"); var docCache = require("../model/docCache"); var cursorHistory = require("../../cursorHistory"); var search = require("../search/monacoSearch"); var semanticView = require("../addons/semanticView"); var monacoUtils = require("../monacoUtils"); var gitStatus = require("../addons/gitStatus"); var liveAnalysis = require("../addons/liveAnalysis"); var quickFix = require("../addons/quickFix"); var linter = require("../addons/linter"); var docblockr = require("../addons/dockblockr"); var doctor = require("../addons/doctor"); var testedMonaco = require("../addons/testedMonaco"); var utils = require("../../../common/utils"); var autoCloseTag = require("../addons/autoCloseTag"); // Any other style modifications require('./codeEditor.css'); exports.monokai = [ { token: '', foreground: 'f8f8f2' }, { token: 'comment', foreground: '75715e' }, { token: 'string', foreground: 'e6db74' }, { token: 'support.property-value.string.value.json', foreground: 'e6db74' }, { token: 'constant.numeric', foreground: 'ae81ff' }, { token: 'constant.language', foreground: 'ae81ff' }, { token: 'constant.character', foreground: 'ae81ff' }, { token: 'constant.other', foreground: 'ae81ff' }, { token: 'keyword', foreground: 'f92672' }, { token: 'support.property-value.keyword.json', foreground: 'f92672' }, { token: 'storage', foreground: 'aae354' }, { token: 'storage.type', foreground: '66d9ef', fontStyle: 'italic' }, { token: 'entity.name.class', foreground: 'a6e22e' }, { token: 'entity.other', foreground: 'a6e22e' }, { token: 'entity.name.function', foreground: 'a6e22e' }, { token: 'entity.name.tag', foreground: 'f92672' }, { token: 'entity.other.attribute-name', foreground: 'a6e22e' }, { token: 'variable', foreground: 'f8f8f2' }, { token: 'variable.parameter', foreground: 'fd971f', fontStyle: 'italic' }, { token: 'support.function', foreground: '66d9ef' }, { token: 'support.constant', foreground: '66d9ef' }, { token: 'support.type', foreground: '66d9ef' }, { token: 'support.class', foreground: '66d9ef', fontStyle: 'italic' }, /** We use qualifier for `const`, `var`, `private` etc. */ { token: 'qualifier', foreground: '00d0ff' }, /* `def` does not exist. We like to use it for variable definitions */ { token: 'def', foreground: 'fd971f' }, /** variable-2 doesn't exist. We use it for identifiers in type positions */ { token: 'variable-2', foreground: '9effff' }, ]; monaco.editor.defineTheme('monokai', { base: 'vs-dark', inherit: true, rules: exports.monokai }); var CodeEditor = /** @class */ (function (_super) { __extends(CodeEditor, _super); function CodeEditor(props) { var _this = _super.call(this, props) || this; /** Ready after the doc is loaded */ _this.ready = false; _this.afterReadyQueue = []; /** If already ready it execs ... otherwise waits */ _this.afterReady = function (cb) { if (_this.ready) cb(); else { _this.afterReadyQueue.push(cb); } }; _this.firstFocus = true; _this.focus = function () { if (!_this.ready && _this.firstFocus) { _this.firstFocus = false; _this.afterReadyQueue.push(function () { _this.resize(); _this.focus(); }); } else if (_this.editor) { _this.editor.focus(); } }; _this.resize = function () { if (_this.editor) { var before_1 = _this.editor.getDomNode().scrollHeight; _this.refresh(); var after_1 = _this.editor.getDomNode().scrollHeight; var worthRestoringScrollPosition = (after_1 !== before_1) && (after_1 != 0); /** Restore last scroll position on refresh after a blur */ if (_this.lastScrollPosition != undefined && worthRestoringScrollPosition) { setTimeout(function () { _this.editor.setScrollTop(_this.lastScrollPosition); // console.log(this.props.filePath, before, after, worthRestoringScrollPosition, this.lastScrollPosition); // DEBUG _this.lastScrollPosition = undefined; }); } } }; _this.lastScrollPosition = undefined; _this.gotoPosition = function (position) { _this.afterReady(function () { _this.lastScrollPosition = undefined; // Don't even think about restoring scroll position /** e.g. if the tab is already active we don't call `focus` from `gotoPosition` ... however it might not have focus */ if (!_this.editor.isFocused()) { _this.editor.focus(); } /** SetTimeout because if editor comes out of hidden state we goto position too fast and then the scroll position is off */ setTimeout(function () { return monacoUtils.gotoPosition({ editor: _this.editor, position: position }); }); }); }; _this.refresh = function () { _this.editor.layout(); }; _this.focusChanged = function (focused) { _this.setState({ isFocused: focused }); _this.props.onFocusChange && _this.props.onFocusChange(focused); }; _this.search = function (options) { search.commands.search(_this.editor, options); }; _this.hideSearch = function () { search.commands.hideSearch(_this.editor); }; _this.findNext = function (options) { search.commands.findNext(_this.editor, options); }; _this.findPrevious = function (options) { search.commands.findPrevious(_this.editor, options); }; _this.replaceNext = function (newText) { search.commands.replaceNext(_this.editor, newText); }; _this.replacePrevious = function (newText) { search.commands.replacePrevious(_this.editor, newText); }; _this.replaceAll = function (newText) { search.commands.replaceAll(_this.editor, newText); }; _this.handleCursorActivity = function () { var cursor = _this.editor.getSelection(); cursorHistory.addEntry({ line: cursor.startLineNumber - 1, ch: cursor.startColumn - 1, }); }; _this.state = { isFocused: false, loading: true, }; return _this; } CodeEditor.prototype.componentDidMount = function () { var _this = this; var mountNode = this.refs.codeEditor; var filePath = this.props.filePath; this.editor = monaco.editor.create(mountNode, { value: '...', theme: 'monokai', folding: true, autoClosingBrackets: true, wrappingColumn: 0, readOnly: false, scrollBeyondLastLine: false, formatOnType: true, contextmenu: false, /** Move snippet suggestions to the bottom */ snippetSuggestions: 'bottom', /** Since everything else in our UI is Square */ roundedSelection: false, /** For git status, find results, errors */ overviewRulerLanes: 3, /** Don't reserve too much space for line numbers */ lineNumbersMinChars: 4, /** We need the glyph margin to show live analysis stuff */ glyphMargin: true, /** * Change the default font. * The default is `consolas` , `courier new`. * This means that if user does not have consolas they get *aweful* courier new. * Don't want that. * Also the default change by OS. * I prefer consistency so going with custom font everywhere */ fontFamily: 'consolas, menlo, monospace', /** Also make the font a bit bigger */ fontSize: 16, }, []); this.editor.filePath = filePath; // Utility to load editor options var loadEditorOptions = function (editorOptions) { // Feels consistent with https://code.visualstudio.com/Docs/customization/userandworkspace _this.editor.getModel().updateOptions({ insertSpaces: editorOptions.convertTabsToSpaces, tabSize: editorOptions.tabSize }); }; this.disposible.add(socketClient_1.cast.editorOptionsChanged.on(function (res) { if (res.filePath === _this.props.filePath) { loadEditorOptions(res.editorOptions); } })); // load up the doc docCache.getLinkedDoc(this.props.filePath, this.editor).then(function (_a) { var doc = _a.doc, editorOptions = _a.editorOptions; // Load editor options loadEditorOptions(editorOptions); if (_this.props.preview) { _this.gotoPreview(_this.props.preview); } // linter // NOTE: done here because it depends on model :) _this.disposible.add(linter.setup(_this.editor)); /** Tested */ if (!_this.props.readOnly) { _this.disposible.add(testedMonaco.setup(_this.editor)); } /** Auto close tag */ var ext = utils.getExt(_this.props.filePath); if (ext === 'tsx') { _this.disposible.add(autoCloseTag.setup(_this.editor)); } // Mark as ready and do anything that was waiting for ready to occur 🌹 _this.afterReadyQueue.forEach(function (cb) { return cb(); }); _this.ready = true; _this.setState({ loading: false }); }); this.disposible.add(this.editor.onDidFocusEditor(this.focusChanged.bind(this, true))); this.disposible.add(this.editor.onDidBlurEditor(this.focusChanged.bind(this, false))); // cursor history if (!this.props.readOnly) { this.disposible.add(this.editor.onDidChangeCursorPosition(this.handleCursorActivity)); } // live analysis this.disposible.add(liveAnalysis.setup(this.editor)); // quick fix if (!this.props.readOnly) { this.disposible.add(quickFix.setup(this.editor)); } // Git status this.disposible.add(gitStatus.setup(this.editor)); // Docblockr this.disposible.add(docblockr.setup(this.editor)); }; CodeEditor.prototype.componentWillUnmount = function () { _super.prototype.componentWillUnmount.call(this); docCache.removeLinkedDoc(this.props.filePath, this.editor); this.editor.dispose(); this.editor = null; }; CodeEditor.prototype.willBlur = function () { this.lastScrollPosition = this.editor.getScrollTop(); // console.log('Storing:', this.props.filePath, this.lastScrollPosition); // DEBUG }; CodeEditor.prototype.getValue = function () { this.editor.getValue(); }; /** * used to seed the initial search if coming out of hidden */ CodeEditor.prototype.getSelectionSearchString = function () { var selection = this.editor.getSelection(); if (selection.startLineNumber === selection.endLineNumber) { if (selection.isEmpty()) { var wordAtPosition = this.editor.getModel().getWordAtPosition(selection.getStartPosition()); if (wordAtPosition) { return wordAtPosition.word; } } else { return this.editor.getModel().getValueInRange(selection); } } return undefined; }; CodeEditor.prototype.render = function () { var className = 'ReactCodeEditor'; if (this.state.isFocused) { className += ' ReactCodeEditor--focused'; } var loadingStyle = { position: 'absolute', top: '45%', left: '45%', zIndex: 1, color: '#999', border: '5px solid #999', borderRadius: '5px', fontSize: '2rem', padding: '5px', transition: '.2s opacity', opacity: this.state.loading ? 1 : 0, pointerEvents: 'none', }; return (React.createElement("div", { className: className, style: csx.extend(csx.horizontal, csx.flex, { position: 'relative', maxWidth: '100%' }) }, !this.props.readOnly && React.createElement(doctor.Doctor, { cm: this.editor, filePath: this.props.filePath }), React.createElement("div", { style: loadingStyle }, "LOADING"), React.createElement("div", { ref: "codeEditor", style: { display: 'flex', flexDirection: 'column', flex: 1, overflow: 'hidden' } }), !this.props.readOnly && React.createElement(semanticView.SemanticView, { editor: this.editor, filePath: this.props.filePath }))); }; CodeEditor.prototype.componentWillReceiveProps = function (nextProps) { // If next props are getting a preview then old props had them too (based on how we use preview) if (nextProps.preview && nextProps.preview.start !== this.props.preview.start) { this.gotoPreview(nextProps.preview); } }; CodeEditor.prototype.gotoPreview = function (preview) { // Re-layout as for preview style editors monaco seems to render faster than CSS 🌹 this.editor.layout(); var pos = this.editor.getModel().getPositionAt(preview.start); this.editor.revealLineInCenterIfOutsideViewport(pos.lineNumber); this.editor.setPosition(pos); }; return CodeEditor; }(ui.BaseComponent)); exports.CodeEditor = CodeEditor;