SOURCE

console 命令行工具 X clear

                    
>
console
const _module = (function () {
    let factories = {};
    let modules = {};
    function search(folder, path) {
        if (!path.includes(".")) {
            return require(path);
        }
        let stack = folder.split("/");
        let folders = path.split("/");
        folders.forEach(folder => {
            if (folder == "..") {
                stack.pop();
            } else if (folder && folder != ".") {
                stack.push(folder);
            }
        });
        return require(stack.join("/"));
    }
    function require(name, dep, callback) {
        let $module;
        let deps = [];
        let $require = function (path) {
            return search(name, path);
        }
        let _module = {
            get exports() {
                return modules[name];
            },
            set exports(value) {
                modules[name] = value;
            }
        }
        if (!modules[name]) {
            if (factories[name] instanceof Function) {
                modules[name] = {};
                factories[name](
                    $require,
                    modules[name],
                    _module
                );
            }
        }
        $module = modules[name];
        if (Array.isArray(dep)) {
            dep.forEach(folder => {
                switch (folder) {
                    case "require":
                        deps.push($require);
                        break;
                    case "exports":
                        deps.push($module);
                        break;
                    case "module":
                        deps.push(_module);
                        break;
                    default:
                        deps.push($require(folder));
                        break;
                }
            });
        }
        if (typeof callback != "function") {
            callback = arguments[arguments.length - 1];
        }
        if (typeof callback == "function") {
            callback.apply(null, [$module].concat(deps));
        }
        return $module;
    }
    function define(name, factory) {
        factories[name] = factory;
    }
    return { define, require, factories, modules };
})();
const { define, require } = _module;

define("editor/native_extend", function (require, exports, module) {
    (function () {
        this.on = function (type, listener, option) {
            this.addEventListener(type, listener, option);
        }
        this.off = function (type, listener) {
            this.removeEventListener(type, listener);
        }
        this.once = function (type, listener, option) {
            if (!option) {
                option = {};
            }
            option.once = true;
            this.addEventListener(type, listener, option);
        }
    }).call(EventTarget.prototype)
        ; (function () {
            this.extractChildElement = function () {
                let children = Array.from(this.children);
                this.innerHTML = "";
                return children;
            };
            this.setStyles = function (option) {
                let $self = this;
                Object.keys(option).forEach(function (key) {
                    $self.style[key] = option[key];
                });
            }
            this.addClass = function () {
                let classes = Array.from(arguments);
                this.classList.add.apply(this.classList, classes);
            }
            this.removeClass = function () {
                let classes = Array.from(arguments);
                this.classList.remove.apply(this.classList, classes);
            }
        }).call(HTMLElement.prototype);
    (function () {
        this.isValidCSSValue = function (key, value) {
            let el = this.createElement("div");
            el.style[key] = value;
            return el.style[key] != "";
        }
        this.buildElement = function (option) {
            let el = option.tag instanceof HTMLElement ? option.tag : this.createElement(option.tag);
            if (option.content != undefined) {
                el.innerHTML = option.content;
            }
            if (option.clazz instanceof Array) {
                el.classList.add(...option.clazz);
            } else if (typeof option.clazz === 'string') {
                el.classList.add(option.clazz);
            }
            if (option.style instanceof Object) {
                Object.keys(option.style).forEach(key => {
                    el.style[key] = option.style[key];
                });
            }
            if (option.parent && option.parent.appendChild) {
                option.parent.appendChild(el);
            } else if (typeof option.parent == "string") {
                this.querySelector(option.parent).appendChild(el);
            }
            if (option.attr instanceof Object) {
                Object.keys(option.attr).forEach(key => {
                    el.setAttribute(key, option.attr[key]);
                });
            }
            if (option.prop instanceof Object) {
                Object.keys(option.prop).forEach(key => {
                    el[key] = option.prop[key];
                });
            }
            return el;
        }
    }).call(Document.prototype);
    (function () {
        this.toMap = function (callback) {
            let map = {};
            for (let i = 0; i < this.length; ++i) {
                if (typeof callback === "function") {
                    let result = callback(this[i], i, this);
                    if (result) {
                        map[result.key] = result.value;
                    }
                } else {
                    map[this[i]] = this[i];
                }
            }
            return map;
        }
        this.empty = function () {
            this.length = 0;
        }
    }).call(Array.prototype)
});
define("editor/utils", function (require, exports, module) {
    exports.isFunction = function (fnc) {
        return typeof fnc == "function";
    }
    exports.addEventListener = function (target, type, listener, option) {
        if (target instanceof EventTarget) {
            target.addEventListener(type, listener, option);
        } else if (exports.isFunction(target.on)) {
            target.on(type, listener);
        }
    }
    exports.removeEventListener = function (target, type, listener) {
        if (target instanceof EventTarget) {
            target.removeEventListener(type, listener);
        } else if (exports.isFunction(target.off)) {
            target.off(type, listener);
        }
    }
    exports.addOnceEventListener = function (target, type, listener) {
        if (target instanceof EventTarget) {
            target.addEventListener(type, listener, { once: true });
        } else if (exports.isFunction(target.once)) {
            target.once(type, listener);
        } else if (exports.isFunction(target.on) && exports.isFunction(target.off)) {
            function callback() {
                listener.apply(this, Array.from(arguments));
                target.off(type, callback);
            }
            target.on(type, callback);
        }
    }

    let testCSSValidElem = document.createElement("div");
    exports.isValidCSSValue = function (key, value) {
        let valid;
        testCSSValidElem.style[key] = value;
        valid = testCSSValidElem.style[key] != "";
        testCSSValidElem.style[key] = "";
        return valid;
    }
    exports.buildElement = function (option) {
        let el = option.tag instanceof HTMLElement ? option.tag : document.createElement(option.tag);
        if (option.content != undefined) {
            el.innerHTML = option.content;
        }
        if (option.clazz instanceof Array) {
            el.classList.add(...option.clazz);
        } else if (typeof option.clazz === 'string') {
            el.classList.add(option.clazz);
        }
        if (option.style instanceof Object) {
            Object.keys(option.style).forEach(key => {
                el.style[key] = option.style[key];
            });
        }
        if (option.parent && option.parent.appendChild) {
            option.parent.appendChild(el);
        } else if (typeof option.parent == "string") {
            document.querySelector(option.parent).appendChild(el);
        }
        if (option.attr instanceof Object) {
            Object.keys(option.attr).forEach(key => {
                el.setAttribute(key, option.attr[key]);
            });
        }
        if (option.prop instanceof Object) {
            Object.keys(option.prop).forEach(key => {
                el[key] = option.prop[key];
            });
        }
        return el;
    }
    exports.setElementStyles = function (elem, styles) {
        Object.keys(styles).forEach(key => {
            elem.style[key] = styles[key];
        });
    }
    exports.extractChildren = function (target) {
        let children = Array.from(target.children);
        target.replaceChildren();
        return children;
    }
    exports.addClass = function (target, classes) {
        if (!Array.isArray(classes)) {
            classes = Array.from(arguments).slice(1);
        }
        target.classList.add.apply(target.classList, classes);
    }
    exports.removeClass = function (target, classes) {
        if (!Array.isArray(classes)) {
            classes = Array.from(arguments).slice(1);
        }
        target.classList.remove.apply(target.classList, classes);
    }

    exports.ArrayToMap = function (array, callback) {
        let map = {};
        for (let i = 0; i < array.length; ++i) {
            if (typeof callback === "function") {
                let result = callback(array[i], i, array);
                if (result) {
                    map[result.key] = result.value;
                }
            } else {
                map[array[i]] = array[i];
            }
        }
        return map;
    }
    exports.emptyArray = function (array) {
        array.length = 0;
    }

    exports.deepCopy = function (target, source) {
        for (let key in source) {
            let value = source[key];
            if (value instanceof Array) {
                target[key] = [];
                deepCopy(target[key], value);
            } else if (value instanceof Object) {
                if (typeof value == "function") {
                    target[key] = value;
                } else {
                    target[key] = {};
                    deepCopy(target[key], value);
                }
            } else {
                target[key] = value;
            }
        }
    }

    exports.fncToDebounced = function (fnc, time, thisArg) {
        function callback() {
            clearTimeout(callback.timer);
            callback.timer = setTimeout(() => {
                fnc.apply(thisArg, Array.from(arguments));
            }, time);
        }

        callback.cancel = function () {
            clearTimeout(callback.timer);
        }
        callback.immediate = function () {
            fnc.apply(thisArg, Array.from(arguments));
        }

        return callback;
    }
    exports.fncToThrottled = function (fnc, time, leading, thisArg) {
        let args = [];
        function callback() {
            args = Array.from(arguments);
            if (callback.waiting) {
                return;
            }
            callback.waiting = true;
            if (leading) {
                fnc.apply(thisArg, args);
            }
            clearTimeout(callback.timer);
            callback.timer = setTimeout(() => {
                callback.waiting = false;
                fnc.apply(thisArg, args);
            }, time);
        }
        callback.cancel = function () {
            clearTimeout(callback.timer);
        }
        callback.immediate = function () {
            fnc.apply(thisArg, Array.from(arguments));
        }
        return callback;
    }
    exports.fncToLater = function (fnc, time, thisArg) {
        function callback() {
            callback.args = Array.from(arguments);
            if (callback.pendding) {
                return;
            }
            callback.pendding = true;
            callback.timer = setTimeout(() => {
                fnc.apply(thisArg, callback.args);
                callback.pendding = false;
            }, time);
        }
        callback.immediate = function () {
            callback.args = Array.from(arguments);
            fnc.apply(thisArg, callback.args);
        }
        callback.cancel = function () {
            clearTimeout(callback.timer);
        }
        return callback;
    }
    exports.comparePoints = function (p1, p2) {
        let value = p1.row - p2.row || p1.column - p2.column;
        if (value < -1) {
            value = -1;
        }
        if (value > 1) {
            value = 1;
        }
        return value;
    }
    exports.comparePoint = function (point, range) {
        let isBackwards = exports.isRangeBackwards(range);
        let startPoint = range.start, endPoint = range.end;
        if (isBackwards) {
            startPoint = range.end;
            endPoint = range.start;
        }
        if (exports.comparePoints(point, startPoint) >= 0 && exports.comparePoints(point, endPoint) <= 0) {
            return 0;
        }
        if (exports.comparePoints(point, startPoint) < 0) {
            return -1;
        }
        if (exports.comparePoints(point, endPoint) > 0) {
            return 1;
        }
    }
    exports.compareRange = function (range, other) {
        let cmp = exports.comparePoint(other.start, range) + exports.comparePoint(other.end, range);
        if (cmp > 1) {
            return "before";
        }
        if (Math.abs(cmp) == 1) {
            return "intersecting";
        } 
        if (cmp == 0) {
            return "inside";
        }
        if (cmp < 0) {
            return "after";
        }
    }
    exports.isRangeBackwards = function (range) {
        if (exports.isFunction(range.isBackwards)) {
            return range.isBackwards();
        }
        return exports.comparePoints(range.end, range.start) < 0;
    }
    exports.setRangeForward = function (range) {
        if (exports.isRangeBackwards(range)) {
            let oldStartRow = range.start.row;
            let oldStartColumn = range.start.column;
            let oldEndRow = range.end.row;
            let oldEndColumn = range.end.column;
            range.setStart(oldEndRow, oldEndColumn);
            range.setEnd(oldStartRow, oldStartColumn);
        }
    }
    exports.setRangeBackward = function (range) {
        if (!exports.isRangeBackwards(range)) {
            let oldStartRow = range.start.row;
            let oldStartColumn = range.start.column;
            let oldEndRow = range.end.row;
            let oldEndColumn = range.end.column;
            range.setEnd(oldStartRow, oldStartColumn);
            range.setStart(oldEndRow, oldEndColumn);
        }
    }
    exports.captureSelectMouse = function(mouseTarget, onMouseDown, onMouseMove, onMouseUp, scrollTarget, onScroll) {
        let mouseEvent = null;
        function _onMouseMove(e) {
            mouseEvent = e;
            if (typeof onMouseMove == "function") {
                onMouseMove.call(this, e);
            }
        }
        function _onMouseUp(e) {
            document.off("mousemove", _onMouseMove);
            if (typeof onMouseUp == "function") {
                onMouseUp.call(this, e);
            }
            if (scrollTarget) {
                scrollTarget.off("scroll", _onScroll);
            }
            mouseEvent = null;
        }
        function _onScroll(e) {
            if (typeof onScroll == "function") {
                onScroll.call(this, mouseEvent, e);
            }
        }
        if (scrollTarget) {
            scrollTarget.on("scroll", _onScroll);
        }
        if (mouseTarget) {
            mouseTarget.once("mousedown", function (e) {
                mouseEvent = e;
                if (typeof onMouseDown == "function") {
                    onMouseDown.call(this, e);
                }
            });
        }
        document.on("mousemove", _onMouseMove);
        document.once("mouseup", _onMouseUp);
    }
});
define("editor/font_metrics", function (require, exports, module) {
    let utils = require('../utils');
    module.exports = class FontMetrics {
        constructor(parent) {
            this.measureNode = utils.buildElement({
                tag: "pre",
                style: {
                    position: "fixed",
                    left: 0,
                    top: 0,
                    opacity: 0,
                    pointerEvents: "none",
                    margin: 0,
                    padding: 0,
                    zIndex: -1
                },
                parent
            });
            this.sizes = {};
        }
        getCharaterWidth(char) {
            if (!this.sizes[char]) {
                this.measureNode.innerText = char;
                this.sizes[char] = this.measureNode.getBoundingClientRect().width;
            }
            return this.sizes[char];
        }
        getTextWdith(text) {
            let width = 0;
            for (let char of text) {
                width += this.getCharaterWidth(char);
            }
            return width;
        }
        setMeasureNodeStyle(style) {
            let oldWidth = this.getCharaterWidth("x");
            utils.setElementStyles(this.measureNode, style);
            delete this.sizes['x'];
            if (this.getCharaterWidth("x") != oldWidth) {
                this.sizes = {};
            }
        }
    }
});
define("editor/emitter", function (require, exports, module) {
    module.exports = class Emitter {
        constructor() {
            this._eventRegistry = {};
        }
        on(type, listener) {
            let listenerList = this._eventRegistry[type] || (this._eventRegistry[type] = []);
            listenerList.push(listener);
        }
        off(type, listener) {
            let listenerList = this._eventRegistry[type];
            if (Array.isArray(listenerList)) {
                let index = listenerList.indexOf(listener);
                if (index != -1) {
                    listenerList.splice(index, 1);
                }
            }
        }
        once(type, listener) {
            let $self = this;
            function callback() {
                listener.apply($self, Array.from(arguments));
                $self.off(type, callback);
            }
            this.on(type, callback);
        }
        emit(type) {
            let listenerList = this._eventRegistry[type];
            if (Array.isArray(listenerList)) {
                let args = Array.prototype.slice.call(arguments, 1);
                listenerList.forEach(callback => {
                    callback.apply(this, args);
                });
            }
        }
        removeAllListeners(type) {
            if (type && typeof type == "string" &&
                type in this._eventRegistry) {
                this._eventRegistry[type] = [];
            } else {
                this._eventRegistry = {};
            }
        }
    }
});
define("editor/config", function (require, exports, module) {
    module.exports = class Config {
        constructor(renderer) {
            this.renderer = renderer;
            this.opts = {};
            this.$toExec = {};
            this.$pendding = false;
        }
        defineOption(key, desc) {
            this.opts[key] = { ...desc };
            if (desc.immediate && desc.handler) {
                desc.handler.call(this, { key, oldValue: undefined, newValue: desc.value });
            }
        }
        setOption(key, value) {
            let desc = this.opts[key];
            if (!desc) {
                return;
            }
            let mutation = { key, newValue: value, oldValue: desc.value };
            desc.value = value;
            if (desc.handler && !desc.async) {
                desc.handler.call(this, mutation);
            } else if (desc.handler && desc.async) {
                this.$toExec[key] = {
                    handler: desc.handler,
                    mutation
                };
            }
            this._schedule();
        }
        getOption(key) {
            let desc = this.opts[key];
            if (!desc) {
                return;
            }
            return desc.value;
        }
        _schedule() {
            if (this.$pendding) {
                return;
            }
            this.$pendding = true;
            setTimeout(() => {
                this._flush();
            });
        }
        _flush() {
            for (let key in this.$toExec) {
                let handler = this.$toExec[key].handler;
                let mutaion = this.$toExec[key].mutaion
                handler.call(this, mutaion);
            }
            this.$pendding = false;
            this.$toExec = {};
        }
    }
});
define("editor/config/default_options", function (require, exports, module) {
    module.exports = function defaultOptions(config) {
        config.defineOption("fontSize", {
            value: 14,
            async: true,
            immediate: true,
            handler(mutaion) {
                let renderer = this.renderer;
                let metrics = renderer.metrics;
                let container = renderer.container;
                metrics.setMeasureNodeStyle({
                    fontSize: mutaion.newValue + 'px'
                });
                container.style.fontSize = mutaion.newValue + 'px';
            },
        });
        config.defineOption("fontFamily", {
            value: "Consolas",
            async: true,
            immediate: true,
            handler(mutaion) {
                let renderer = this.renderer;
                let metrics = renderer.metrics;
                let container = renderer.container;
                metrics.setMeasureNodeStyle({
                    fontFamily: mutaion.newValue
                });
                container.style.fontFamily = mutaion.newValue;
            }
        });
        config.defineOption("startLine", {
            value: 1,
            async: true,
            handler(mutaion) {
                this.renderer.$gutter.update();
            }
        });
        config.defineOption("lineHeight", {
            value: 20,
            async: true,
            immediate: true,
            handler(mutaion) {
                let renderer = this.renderer;
                let content = renderer.content;
                let newLineHeight = mutaion.newValue + 'px';
                content.style.lineHeight = newLineHeight;
                renderer.$gutter.setLineHeight(newLineHeight);
            }
        });
        config.defineOption("selectionBackground", {
            value: "rgba(0,0,0,.3)",
            async: true
        });
        config.defineOption("cursorColor", {
            value: "#000",
            async: true
        });
        config.defineOption("cursorWidth", {
            value: 1,
            async: true
        });
        return config;
    }
});
define("editor/render_descriptor", function (require, exports, module) {
    let utils = require('../utils');
    exports.addDefaultDescriptor = function (loop) {
        loop.addRenderDescriptor({
            setData(arg) {

            },
            exec(renderer) {
                renderer.$gutter.update();
            },
            name: "change_gutter"
        });
        loop.addRenderDescriptor({
            setData(arg) {

            },
            exec: function(renderer) {
                renderer.$marker.update();
                renderer.$cursor.update();
                if (renderer.$textInput.isFocus) {
                    renderer.$cursor.pause();
                }
            },
            name: "change_selection"
        });
        loop.addRenderDescriptor({
            setData(arg) {

            },
            exec(renderer) {
                renderer.$text.update();
            },
            name: "change_text"
        });
        loop.addRenderDescriptor({
            exec(renderer) {
                renderer.$scrollbar.updateScrollHeight();
            },
            name: "change_scroll_height"
        });
        loop.addRenderDescriptor({
            exec(renderer) {
                renderer.$scrollbar.updateScrollWidth();
            },
            name: "change_scroll_width"
        });
        loop.addRenderDescriptor({
            exec(renderer) {
                renderer.scrollContentY();
            },
            name: "change_scroll_v"
        });
        loop.addRenderDescriptor({
            exec(renderer) {

            },
            name: "change_scroll_h"
        });
        loop.addRenderDescriptor({
            exec(renderer, types) {
                if (!types["change_gutter"]) {
                    renderer.$gutter.update();
                }
                if (!types["change_selection"]) {
                    renderer.$marker.update();
                    renderer.$cursor.update();
                }
                if (!types["change_scroll_height"]) {
                    renderer.$scrollbar.updateScrollHeight();
                }
                if (!types["change_scroll_width"]) {
                    renderer.$scrollbar.updateScrollWidth();
                }
                if (!types["change_scroll_v"]) {
                    renderer.scrollContentY();
                }
            },
            name: "change_full"
        });
    }
});
define("editor/renderloop", function (require, exports, module) {
    module.exports = class RenderLoop {
        constructor(renderer) {
            this.renderer = renderer;
            this.pendding = false;
            this.changes = 0;
            this.$desc = [];

            this.flush = (function () {
                let filter = this.getDescriptorByChanges(this.changes);
                let changesMap = filter.toMap(function (type) {
                    return { key: type.name, value: type };
                });
                filter.forEach(type => {
                    if (type.exec instanceof Function) {
                        type.exec(this.renderer, changesMap);
                    }
                });
                this.pendding = false;
                this.changes = 0;
            }).bind(this);
        }
        schedule(changes) {
            if (typeof changes == "string") {
                changes = this.getDescriptorByName(changes);
            } else if (typeof changes != "number") {
                return;
            }
            this.changes |= changes;

            let args = Array.prototype.slice.call(arguments, 1);
            this.getDescriptorByChanges(changes).forEach(type => {
                if (typeof type.setData instanceof Function) {
                    type.setData.apply(type, args);
                }
            });
            if (this.pendding) {
                return;
            }
            this.pendding = true;
            requestAnimationFrame(this.flush);
        }
        addRenderDescriptor(desc) {
            this.$desc.push(desc);
        }
        getDescriptorByName(name) {
            let changes = 0;
            this.$desc.find((t, index) => {
                if (t.name == name) {
                    changes = 2 ** index;
                    return true;
                }
            });
            return changes;
        }
        getDescriptorByChanges(changes) {
            return this.$desc.filter((_, index) => {
                return changes & (2 ** index);
            });
        }
    }
});
define("editor/mouse_handler", function (require, exports, module) {
    let Range = require('../range');
    let utils = require('../utils');
    module.exports = class MouseHandler {
        constructor(renderer) {
            this.renderer = renderer;
            this.session = renderer.session;
            this.mouseEvent = null;
            this.mouseDownEvent = null;
            this.activeRange = null;

            let _self = this;
            let container = renderer.container;
            let scroller = renderer.scroller;
            let textInput = renderer.$textInput;
            let selection = this.session.selection;
            let loop = renderer.$loop;

            container.on("mousedown", function (e) {
                e.preventDefault();
                textInput.focus();
            });
            container.on("wheel", function (e) {
                renderer.$scrollbar.scrollVBy(e.deltaY);
            });
            scroller.on("mousedown", function (e) {
                _self.startSelect(e);
            });
            textInput.on("keydown", function (e) {
                (e.altKey || e.ctrlKey) && e.preventDefault();
            });
            selection.on("change", function () {
                loop.schedule("change_selection");
            });
        }
        startSelect(e) {
            if (e.button != 0 && e.button != 1) {
                return;
            }
            let _self = this;
            let selection = this.session.selection;
            let renderer = this.renderer;
            let pos = renderer.screenToDocumentCoordinates(e.clientX, e.clientY);
            this.mouseEvent = this.mouseDownEvent = e;
            if (e.shiftKey) {
                selection.moveSelectionHead(pos.row,pos.column);
            } else {
                selection.moveTo(pos.row,pos.column);
            }

            let mouseSelectChanged = true;
            function onSelectionInterval() {
                if (mouseSelectChanged) {
                    let event = _self.mouseEvent;
                    let mouseDownEvent = _self.mouseDownEvent;
                    if (event.altKey || (mouseDownEvent && mouseDownEvent.button == 1)) {
                        _self.blockSelect(event);
                    } else if (event.ctrlKey) {
                        _self.multiSelect(event);
                    } else {
                        _self.singleSelect(event);
                    }
                    mouseSelectChanged = false;
                }
            }
            let interval = setInterval(onSelectionInterval, 15);

            function callback(e) {
                let pos = renderer.screenToDocumentCoordinates(e.clientX, e.clientY);
                _self.mouseEvent = e;
                selection.moveSelectionHead(pos.row, pos.column);
                mouseSelectChanged = true;
            }
            function onMouseSelectionEnd() {
                clearInterval(interval);
                onSelectionInterval();
                _self.activeRange = null;
            }

            this.captureMouse(null, null, callback, onMouseSelectionEnd, renderer.$scrollbar, callback);

        }
        blockSelect(e) {
            let renderer = this.renderer;
            let selection = this.session.selection;
            let startPos = selection.anchor;
            let endPos = renderer.screenToDocumentCoordinates(e.clientX, e.clientY);
            let isBackwards = Range.comparePoints(startPos, endPos) > 0;
            let startRow = isBackwards ? endPos.row : startPos.row;
            let endRow = isBackwards ? startPos.row : endPos.row;
            for (let i = startRow; i <= endRow; ++i) {
                if ((i - startRow + 1) > selection.MAX_RANGE_COUNT) {
                    break;
                }
                let rangeIndex = i - startRow;
                let range = selection.getRangeAt(rangeIndex);
                let contains = Boolean(range);
                let column = renderer.screenToRowCoordinate(i, e.clientX);
                if (!contains) {
                    range = new Range(i, startPos.column, i, column);
                    selection.addRange(range);
                }
                if (contains) {
                    range.setStart(i, startPos.column);
                    range.setEnd(i, column);
                }
            }
            selection.limitRangeCount(endRow - startRow + 1);
            renderer.scrollCursorIntoView();
        }
        singleSelect(e) {
            let pos = this.renderer.screenToDocumentCoordinates(e.clientX, e.clientY);
            this.session.selection.selectTo(pos.row, pos.column);
            this.renderer.scrollCursorIntoView();
        }
        multiSelect(e) {
            let renderer = this.renderer;
            let selection = this.session.selection;
            let pos = renderer.screenToDocumentCoordinates(e.clientX, e.clientY);
            let activeRange = this.activeRange;
            if (!activeRange) {
                let toDelete = selection.getRangesFromPoint(pos);
                if (toDelete.length) {
                    for (let range of toDelete) {
                        if (selection.rangeCount() < 2) {
                            break;
                        }
                        selection.removeRange(range);
                    }
                } else {
                    let anchor = selection.anchor;
                    let head = selection.head;
                    let range = new Range(anchor.row, anchor.column, head.row, head.column);
                    selection.addRange(range);
                    this.activeRange = range;
                }
            } else if (activeRange instanceof Range) {
                activeRange.setEnd(pos.row, pos.column);
                let result = selection.merge(activeRange.end.dir);
                for (let item of result) {
                    if (item.removed == activeRange) {
                        this.activeRange = item.range;
                        break;
                    }
                }
                renderer.scrollCursorIntoView();
            }
        }
        captureMouse(mouseTarget, onMouseDown, onMouseMove, onMouseUp, scrollTarget, onScroll) {
            let mouseEvent = null;
            function _onMouseMove(e) {
                mouseEvent = e;
                if (typeof onMouseMove == "function") {
                    onMouseMove.call(this, e);
                }
            }
            function _onMouseUp(e) {
                document.off("mousemove", _onMouseMove);
                if (typeof onMouseUp == "function") {
                    onMouseUp.call(this, e);
                }
                if (scrollTarget) {
                    scrollTarget.off("scroll", _onScroll);
                }
                mouseEvent = null;
            }
            function _onScroll(e) {
                if (typeof onScroll == "function") {
                    onScroll.call(this, mouseEvent, e);
                }
            }
            if (scrollTarget) {
                scrollTarget.on("scroll", _onScroll);
            }
            if (mouseTarget) {
                mouseTarget.once("mousedown", function (e) {
                    mouseEvent = e;
                    if (typeof onMouseDown == "function") {
                        onMouseDown.call(this, e);
                    }
                });
            }
            document.on("mousemove", _onMouseMove);
            document.once("mouseup", _onMouseUp);
        }
    }
});
define("editor/keyboard_handler", function (require, exports, module) {
    module.exports = class KeyboardHandler {
        constructor(renderer) {
            this.renderer = renderer;
            this.session = renderer.session;

            let $textInput = renderer.$textInput,
                selection = this.session.selection;

            $textInput.on("keydown", function (e) {
                let isShift = e.shiftKey;
                let isControl = e.ctrlKey;
                let key = e.key;
                if (key == "ArrowLeft") {
                    if (isShift) {
                        selection.selectLeft();
                    } else {
                        selection.moveCursorLeft();
                    }
                } else if (key == "ArrowRight") {
                    if (isShift) {
                        selection.selectRight();
                    } else {
                        selection.moveCursorRight();
                    }
                } else if (key == "ArrowUp" && isShift) {
                    selection.selectUp();
                } else if (key == "ArrowDown" && isShift) {
                    selection.selectDown();
                } else if (key == "a" && isControl) {
                    selection.selectAll();
                }
            });
        }
    }
});
define("editor/default_handler", function (require, exports, module) {
    let MouseHandler = require('../mouse_handler');
    let KeyboardHandler = require('../keyboard_handler');
    let utils = require('../utils');
    module.exports = class DefaultHandler {
        constructor(renderer) {
            this.renderer = renderer;
            this.session = renderer.session;
            this.$mouseHandler = new MouseHandler(renderer);
            this.$keyboardHandler = new KeyboardHandler(renderer);

            let $scrollbar = renderer.$scrollbar,
                $textInput = renderer.$textInput,
                $loop = renderer.$loop,
                $cursor = renderer.$cursor;

            $scrollbar.on("scrollV", function () {
                $loop.schedule("change_scroll_v");
            });
            $textInput.on("blur", function () {
                $cursor.stop();
                $cursor.layer.setStyles({
                    opacity: 0.5
                });
            });
            $textInput.on("focus", function () {
                $cursor.start();
                $cursor.layer.setStyles({
                    opacity: 1
                });
            });

            this.session.doc.on("change", function (e) {
                console.log(e);
                $loop.schedule("change_text");
            });
        }
    }
});
define("editor/renderer", function (require, exports, module) {
    let FontMetrics = require('../font_metrics');
    let Config = require('../config');
    let defaultOptions = require('../config/default_options');
    let Cursor = require('../layer/cursor');
    let Gutter = require('../layer/gutter');
    let Marker = require('../layer/marker');
    let Text = require('../layer/text');
    let ScrollBar = require('../layer/scrollbar');
    let TextInput = require('../text_input');
    let RenderLoop = require('../renderloop');
    let addDefaultDescriptor = require('../render_descriptor').addDefaultDescriptor;
    let Emitter = require('../emitter');
    let DefaultHandler = require('../default_handler');
    module.exports = class Renderer extends Emitter {
        constructor(parent, session) {
            super();
            let $self = this;
            this.session = session;
            this.container = document.buildElement({
                tag: "div",
                parent,
                clazz: "editor_container"
            });
            this.metrics = new FontMetrics(this.container);
            this.$gutter = new Gutter(this.container, this);
            this.$gutter.on("resize", function (_, width) {
                $self.scroller.setStyles({
                    left: width + 'px',
                    width: `calc(100% - ${width}px)`
                });
                $self.$scrollbar.hscrollbar.setStyles({
                    left: width + 'px',
                    width: `calc(100% - ${width}px)`
                })
            });
            this.scroller = document.buildElement({
                tag: "div",
                parent: this.container,
                clazz: "scroller"
            });
            this.content = document.buildElement({
                tag: "div",
                clazz: "content",
                parent: this.scroller
            });
            this.$marker = new Marker(this.content, this);
            this.$text = new Text(this.content, this);
            this.$cursor = new Cursor(this.content, this);
            this.$scrollbar = new ScrollBar(this.container, this);
            this.$textInput = new TextInput(this.scroller, this);
            this.config = defaultOptions(new Config(this));
            this.$linesWidth = [];
            this.$loop = new RenderLoop(this);
            addDefaultDescriptor(this.$loop);
            this.$handler = new DefaultHandler(this);
            this.$loop.schedule("change_full");
        }
        update() {
            this.$loop.schedule("change_full");
        }
        getScreenRows() {
            let lineHeight = this.config.getOption("lineHeight");
            let scrollTop = this.$scrollbar.getScrollTop();
            let start = Math.floor(scrollTop / lineHeight);
            let height = this.scroller.getBoundingClientRect().height;
            let lineCount = this.session.doc.lineCount();
            let to = Math.min(Math.floor((scrollTop + height) / lineHeight), Math.max(0, lineCount - 1));
            return { start, to };
        }
        getScreenTextLines() {
            let screenRows = this.getScreenRows();
            return this.session.doc.getLines(screenRows.start, screenRows.to + 1);
        }
        scrollContentY() {
            this.$gutter.update();
            this.$marker.update();
            this.$text.update();
            this.$cursor.update();
            this.scrollContentToScrollOffsetY();
        }
        screenToDocumentCoordinates(x, y) {
            let rect = this.scroller.getBoundingClientRect();
            let scrollTop = this.$scrollbar.getScrollTop();
            let scrollLeft = this.$scrollbar.getScrollLeft();
            let clipX = x - rect.x + scrollLeft;
            let clipY = y - rect.y + scrollTop;
            let posX = clipX < 0 ? 0 : clipX;
            let posY = clipY < 0 ? 0 : clipY;
            let lineHeight = this.config.getOption("lineHeight");
            let row = Math.min(Math.floor(posY / lineHeight), Math.max(this.session.doc.lineCount() - 1, 0));
            let column = 0;
            let line = this.session.doc.getLine(row);
            let width = 0;
            for (; column < line.length; ++column) {
                let boundLeft = width;
                let charWidth = this.metrics.getTextWdith(line[column]);
                let boundRight = boundLeft + charWidth;
                width += charWidth;
                if (posX >= boundLeft && posX <= boundRight) {
                    if (Math.round((posX - boundLeft) / charWidth)) {
                        column++;
                    }
                    break;
                }
            }
            return { row, column };
        }
        screenToTextCoordinate(line, x) {
            let column = 0;
            let width = 0;
            x = x < 0 ? 0 : x;
            for (; column < line.length; ++column) {
                let boundLeft = width;
                let charWidth = this.measureText(line[column]);
                let boundRight = boundLeft + charWidth;
                width += charWidth;
                if (x >= boundLeft && x <= boundRight) {
                    if (Math.round((x - boundLeft) / charWidth)) {
                        column++;
                    }
                    break;
                }
            }
            return column;
        }
        screenToRowCoordinate(row, x) {
            let rect = this.scroller.getBoundingClientRect();
            let left = x - rect.left + this.$scrollbar.getScrollLeft();
            let line = this.session.doc.getLine(row);
            if (typeof line != "string") {
                return;
            }
            return this.screenToTextCoordinate(line, left);
        }
        measureText(text) {
            return this.metrics.getTextWdith(text);
        }
        getRowTextWidth(row) {
            if (typeof this.$linesWidth[row] != "number") {
                let line = this.session.doc.getLine(row);
                if (typeof line === "string") {
                    this.$linesWidth[row] = this.measureText(line);
                }
            }
            return this.$linesWidth[row];
        }
        isPointInScreen(position) {
            let screenRows = this.getScreenRows();
            return position.row >= screenRows.start && position.row <= screenRows.to;
        }
        getFullyVisibleScreenRow() {
            let lineHeight = this.config.getOption("lineHeight");
            let scrollTop = this.$scrollbar.getScrollTop();
            let remain = parseInt(scrollTop % lineHeight);
            let start = remain ? Math.ceil(scrollTop / lineHeight) : Math.floor(scrollTop / lineHeight);
            let height = this.scroller.getBoundingClientRect().height;
            let lineCount = this.session.doc.lineCount();
            let to = Math.min(Math.floor((scrollTop + height - remain) / lineHeight), Math.max(0, lineCount - 1));
            return { start, to };
        }
        scrollContentToScrollOffsetY() {
            let lineHeight = this.config.getOption('lineHeight');
            let scrollTop = this.$scrollbar.getScrollTop();
            let offset = -(scrollTop % lineHeight) + 'px';
            this.content.setStyles({
                top: offset
            })
            this.$gutter.updateScrollOffset();
        }
        scrollVBy(delta) {
            this.$scrollbar.scrollVBy(delta);
        }
        scrollBy(x, y) {
            //pass
        }
        scrollCursorIntoView() {
            let head = this.session.selection.head;
            let row = head.row;
            let lineHeight = this.config.getOption("lineHeight");
            let top = row * lineHeight;
            let bottom = top + lineHeight;
            let scrollTop = this.$scrollbar.getScrollTop();
            let scrollBottom = this.scroller.getBoundingClientRect().height + scrollTop;
            let remain = Math.floor(scrollTop % lineHeight);
            let boundTop = scrollTop + lineHeight - remain;
            let boundBottom = scrollBottom - lineHeight + remain;
            if (top > boundTop && bottom < boundBottom) {
                return;
            }
            if (top <= boundTop) {
                let offset = -(boundTop - top);
                this.$scrollbar.scrollVBy(offset);
            } else if (bottom >= boundBottom) {
                let offset = bottom - boundBottom + remain;
                this.$scrollbar.scrollVBy(offset);
            }
        }
    }
});
define("editor/layer/gutter", function (require, exports, module) {
    let Emitter = require('../../emitter');
    module.exports = class Gutter extends Emitter {
        constructor(parent, renderer) {
            super();
            this.renderer = renderer;
            this.el = document.buildElement({
                tag: "div",
                parent,
                clazz: "gutter"
            });
            this.layer = document.buildElement({
                tag: "div",
                parent: this.el,
                clazz: "gutter_layer"
            });
            this.width = 0;
        }
        drawLineNumber(start, to) {
            let oldNodes = this.layer.extractChildElement();
            let config = this.renderer.config;
            for (let i = start; i <= to; ++i) {
                let childIndex = i - start;
                let child = oldNodes[childIndex];
                if (!child) {
                    child = document.buildElement({
                        tag: "div",
                        clazz: "line_number"
                    });
                    oldNodes.push(child);
                }
                child.innerHTML = i + config.getOption("startLine");
            }
            this.layer.replaceChildren.apply(this.layer, oldNodes);
        }
        resize() {
            let computedWidth = this.getComputedWidth();
            if (computedWidth != this.width) {
                let oldWidth = this.width;
                this.width = computedWidth;
                this.el.setStyles({ width: computedWidth + 'px' });
                this.emit("resize", oldWidth, computedWidth);
            }
        }
        getComputedWidth() {
            let doc = this.renderer.session.doc;
            let config = this.renderer.config;
            let metrics = this.renderer.metrics;
            let lineCount = doc.lineCount();
            let startLine = config.getOption("startLine");
            let measureText = (lineCount - 1 + startLine).toString();
            return metrics.getTextWdith(measureText) + 25;
        }
        update() {
            let screenRows = this.renderer.getScreenRows();
            this.drawLineNumber(screenRows.start, screenRows.to);
            this.resize();
            this.updateScrollOffset();
        }
        setLineHeight(height) {
            if (typeof height == 'number') {
                height += "px";
            }
            this.el.setStyles({ lineHeight: height });
        }
        $onScroll(scrollTop) {
            let config = this.renderer.config;
            let lineHeight = config.getOption('lineHeight');
            let offset = -(scrollTop % lineHeight) + 'px';
            this.layer.setStyles({ top: offset });
        }
        updateScrollOffset() {
            this.$onScroll(this.renderer.$scrollbar.getScrollTop());
        }
    }
});
define("editor/layer/cursor", function (require, exports, module) {
    module.exports = class Cursor {
        constructor(parent, renderer) {
            this.renderer = renderer;
            this.session = renderer.session;
            this.layer = document.buildElement({
                tag: "div",
                clazz: ["cursor", "layer"],
                parent
            });
            this.timer = null;
            this.visible = true;
            this.interval = 700;
        }
        start() {
            clearInterval(this.timer);
            this.timer = setInterval(() => {
                this.toggle();
            }, this.interval);
        }
        stop() {
            clearInterval(this.timer);
            this.show();
        }
        toggle() {
            if (this.visible) {
                this.hide();
                this.visible = false;
            } else {
                this.show();
                this.visible = true;
            }
        }
        hide() {
            this.layer.style.opacity = 0;
            this.visible = false;
        }
        show() {
            this.layer.style.opacity = 1;
            this.visible = true;
        }
        pause() {
            this.stop();
            this.show();
            this.start();
        }
        update() {
            let ranges = this.session.selection.ranges;
            let oldNodes = this.layer.extractChildElement();
            let childIndex = 0;
            let children = [];
            let screenRows = this.renderer.getScreenRows();
            let config = this.renderer.config;
            for (let i = 0; i < ranges.length; ++i) {
                let range = ranges[i];
                if (!this.renderer.isPointInScreen(range.end)) {
                    continue;
                }
                let endRow = range.end.row;
                let endColumn = range.end.column;
                let color = config.getOption("cursorColor");
                let line = this.session.doc.getLine(endRow);
                let lineHeight = config.getOption("lineHeight");
                let child = oldNodes[childIndex++];
                let left = this.renderer.measureText(line.slice(0, endColumn));
                let top = (endRow - screenRows.start) * lineHeight;
                let width = config.getOption("cursorWidth");
                if (!child) {
                    child = document.buildElement({
                        tag: "div",
                        clazz: "cursor_caret"
                    });
                }
                children.push(child);
                child.setStyles({
                    height: lineHeight + 'px',
                    position: "absolute",
                    top: top + 'px',
                    left: left + 'px',
                    border: `${width}px solid ${color}`,
                    boxSizing: "border-box"
                })
            }
            this.layer.replaceChildren.apply(this.layer, children);
        }
    }
});
define("editor/layer/marker", function (require, exports, module) {
    module.exports = class Marker {
        constructor(parent, renderer) {
            this.renderer = renderer;
            this.session = renderer.session;
            this.layer = document.buildElement({
                tag: "div",
                clazz: ["marker", "layer"],
                parent
            });
        }
        update() {
            let screenRows = this.renderer.getScreenRows();
            let clippedRanges = this.session.selection.clipRange(screenRows.start, screenRows.to + 1);
            let lineIndex = 0;
            let oldNodes = this.layer.extractChildElement();
            let children = [];
            let rect = [];
            let config = this.renderer.config;
            for (let i = 0; i < clippedRanges.length; ++i) {
                let range = clippedRanges[i];
                for (let j = range.start.row; j <= range.end.row; ++j) {
                    let startRow = range.start.row;
                    let startColumn = range.start.column;
                    let endRow = range.end.row;
                    let endColumn = range.end.column;
                    let isStart = j == startRow;
                    let isEnd = j == endRow;
                    let isSame = startRow == endRow;
                    let lineHeight = config.getOption("lineHeight");
                    let width = 0;
                    let height = lineHeight;
                    let left = 0;
                    let top = lineHeight * (j - screenRows.start);
                    let line = this.session.doc.getLine(j);
                    let background = config.getOption("selectionBackground");
                    if (isSame) {
                        left = this.renderer.measureText(line.slice(0, startColumn));
                        width = this.renderer.measureText(line.slice(startColumn, endColumn));
                    } else if (isStart) {
                        left = this.renderer.measureText(line.slice(0, startColumn));
                        width = this.renderer.getRowTextWidth(startRow) - left;
                    } else if (isEnd) {
                        left = 0;
                        width = this.renderer.measureText(line.slice(0, endColumn));
                    } else {
                        left = 0;
                        width = this.renderer.getRowTextWidth(j);
                    }
                    if (!isEnd) {
                        width += this.renderer.measureText("x");
                    }
                    let child = oldNodes[lineIndex++];
                    if (!child) {
                        child = document.buildElement({
                            tag: "div",
                            clazz: "marker_line"
                        });
                    }
                    child.setStyles({
                        width: width + 'px',
                        height: height + 'px',
                        left: left + 'px',
                        top: top + 'px',
                        position: "absolute",
                        background
                    });
                    children.push(child);
                    rect.push({
                        el: child,
                        left,
                        right: left + width
                    });
                }
                this.$markerCornerToRounded(rect);
                rect = [];
            }
            this.layer.replaceChildren.apply(this.layer, children);
        }
        $markerCornerToRounded(rect) {
            rect.forEach((item, index) => {
                let prev = rect[index - 1];
                let next = rect[index + 1];
                let el = item.el;
                if (prev) {
                    if ((prev.left > item.left || prev.right < item.left) && item.left) {
                        el.addClass("btlr");
                    } else {
                        el.removeClass("btlr");
                    }
                    if (prev.left > item.right || prev.right < item.right) {
                        el.addClass("btrr");
                    } else {
                        el.removeClass("btrr");
                    }
                } else {
                    el.addClass("btrr");
                    if (item.left) {
                        el.addClass("btlr");
                    } else {
                        el.removeClass("btlr");
                    }
                }

                if (next) {
                    if ((next.left > item.left || next.right < item.left) && item.left) {
                        el.addClass("bblr");
                    } else {
                        el.removeClass("bblr");
                    }
                    if (next.right < item.right) {
                        el.addClass("bbrr");
                    } else {
                        el.removeClass("bbrr");
                    }
                } else {
                    el.addClass("bbrr");
                }
            });
        }
    }
});
define("editor/layer/text", function (require, exports, module) {
    module.exports = class Text {
        constructor(parent, renderer) {
            this.renderer = renderer;
            this.session = renderer.session;
            this.layer = document.buildElement({
                tag: "div",
                clazz: ["text", "layer"],
                parent
            });
        }
        update() {
            let screenLines = this.renderer.getScreenTextLines();
            let oldNodes = this.layer.extractChildElement();
            for (let i = 0; i < screenLines.length; ++i) {
                let child = oldNodes[i];
                if (!child) {
                    child = document.buildElement({
                        tag: "div",
                        clazz: 'text_line'
                    });
                    oldNodes.push(child);
                }
                child.innerHTML = screenLines[i].replace(/ /g, "&nbsp;");
                child.style.height = this.renderer.config.getOption("lineHeight") + 'px';
            }
            this.layer.replaceChildren.apply(this.layer, oldNodes);
        }
    }
});
define("editor/layer/scrollbar", function (require, exports, module) {
    let Emitter = require('../../emitter');
    module.exports = class Scrollbar extends Emitter {
        constructor(parent, renderer) {
            super();
            let $self = this;
            this.renderer = renderer;
            this.session = renderer.session;
            this.layer = document.buildElement({
                tag: "div",
                clazz: "scrollbar",
                parent
            });
            this.vscrollbar = document.buildElement({
                tag: "div",
                clazz: "vsc",
                parent: this.layer
            });
            this.hscrollbar = document.buildElement({
                tag: "div",
                clazz: "hsc",
                parent: this.layer
            });
            this.vscrollbarHeight = document.buildElement({
                tag: "div",
                parent: this.vscrollbar,
                style: {
                    width: '1px'
                }
            });
            this.hscrollbarWidth = document.buildElement({
                tag: "div",
                parent: this.hscrollbar,
                style: {
                    height: '1px'
                }
            });
            this.scrollTop = 0;
            this.scrollLeft = 0;

            this.vscrollbar.on("scroll", function (e) {
                let old = $self.scrollTop;
                $self.scrollTop = $self.getScrollTop();
                $self.emit("scroll", e);
                $self.emit("scrollV", e, old, $self.scrollTop);
            });
            this.hscrollbar.on("scroll", function (e) {
                $self.emit("scroll", e);
                $self.emit("scrollH", e);
            });
        }
        setScrollHeight(height) {
            this.vscrollbarHeight.setStyles({
                height: typeof height == "number" ? height + 'px' : height
            });
        }
        setScrollWidth(width) {
            this.hscrollbarWidth.setStyles({
                width: typeof width == "number" ? width + 'px' : width
            });
        }
        getScrollTop() {
            return this.vscrollbar.scrollTop;
        }
        getScrollLeft() {
            return this.hscrollbar.scrollLeft;
        }
        updateScrollHeight() {
            let config = this.renderer.config;
            let height = this.session.doc.lineCount() * config.getOption("lineHeight");
            this.setScrollHeight(height);
        }
        updateScrollWidth() {
            //pass
        }
        scrollV(scrollTop) {
            this.vscrollbar.scroll(0, scrollTop);
        }
        scrollVBy(delta) {
            this.vscrollbar.scrollBy(0, delta);
        }
    }
});
define("editor/text_input", function (require, exports, module) {
    let Emitter = require('../emitter');
    module.exports = class TextInput extends Emitter {
        constructor(parent, renderer) {
            super();
            let $self = this;
            this.isFocus = false;
            this.renderer = renderer;
            this.textarea = document.buildElement({
                tag: "textarea",
                parent,
                style: {
                    position: "absolute",
                    left: 0,
                    top: 0,
                    opacity: 0,
                    border: "none",
                    outline: "none",
                    margin: 0,
                    padding: 0,
                    width: "1px",
                    height: "1px",
                    fontSize: "1px",
                    resize: "none"
                }
            });
            this.textarea.on("focus", function (e) {
                $self.isFocus = true;
                $self.emit("focus", e);
            });
            this.textarea.on("blur", function (e) {
                $self.isFocus = false;
                $self.emit("blur", e);
            });
            this.textarea.on("keydown", function (e) {
                $self.emit("keydown", e);
            });
            this.textarea.on("keyup", function (e) {
                $self.emit("keyup", e);
            });
            this.isFocus = document.activeElement == this.textarea;
        }
        focus(opt) {
            this.textarea.focus(opt);
        }
        blur() {
            this.textarea.blur();
        }
        moveToCursor() {

        }
    }
});
define("editor/doc", function (require, exports, module) {
    let Emitter = require('../emitter');
    let Range = require('../range');
    module.exports = class Doc extends Emitter {
        constructor(text) {
            super();
            this.lines = [""];
            if (typeof text == "string") {
                this.lines = text.split("\n");
            }
        }
        lineCount() {
            return this.lines.length;
        }
        getLines(start, end) {
            return this.lines.slice(start, end);
        }
        iter(callback) {
            this.lines.forEach(callback);
        }
        getLine(row) {
            return this.lines[row];
        }
        insert(row, column, lines) {
            let clippedPos = this.clipPositionToDocument(row, column);
            let line = this.getLine(clippedPos.row);
            this.lines.splice(clippedPos.row, 1, ...lines);
            this.lines[clippedPos.row] = line.slice(0, clippedPos.column) + this.lines[clippedPos.row];
            this.lines[clippedPos.row + lines.length - 1] += line.slice(clippedPos.column);
            this.emit("change", {
                action: "insert",
                row: clippedPos.row,
                column: clippedPos.column,
                lines
            });
        }
        removeLeft(row, column) {
            let startRow = row;
            let startColumn = column;
            if (column == 0) {
                let line = this.getLine(--row) || "";
                column = line.length;
            }
            this.remove(startRow, startColumn, row, column);
        }
        removeRight(row, column) {
            let startRow = row;
            let startColumn = column;
            let line = this.getLine(row) || "";
            if (startColumn >= line.length) {
                startColumn = 0;
                ++row;
            }
            this.remove(startRow, startColumn, row, column);
        }
        remove(startRow, startColumn, endRow, endColumn) {
            let clipStart = this.clipPositionToDocument(startRow, startColumn);
            let clipEnd = this.clipPositionToDocument(endRow, endColumn);
            let isBackwards = Range.comparePoints(clipStart, clipEnd) > 0;
            let startLine = this.getLine(clipStart.row);
            let endLine = this.getLine(clipEnd.row);
            let _startRow = isBackwards ? clipEnd.row : clipStart.row;
            let _endRow = isBackwards ? clipStart.row : clipEnd.row;
            let _startColumn = isBackwards ? clipEnd.column : clipStart.column;
            let _endColumn = isBackwards ? clipStart.column : clipEnd.column;
            let deleteCount = _endRow - _startRow + 1;
            let insertLine = startLine.slice(0, _startColumn) + endLine.slice(_endColumn);
            let removed = this.lines.splice(_startRow, deleteCount, insertLine);
            this.emit("change", {
                action: "remove",
                startRow,
                startColumn,
                endRow,
                endColumn,
                removedLines: removed
            });
        }
        clipPositionToDocument(row, column) {
            let pos = row instanceof Object ? { ...row } : { row, column };
            let maxRow = Math.max(0, this.lineCount() - 1);
            if (pos.row < 0) {
                pos.row = 0;
                pos.column = 0;
            } else if (pos.row > maxRow) {
                pos.row = maxRow;
                pos.column = this.getLine(maxRow).length;
            } else {
                let line = this.getLine(pos.row);
                pos.column = Math.min(line.length, Math.max(pos.column, 0));
            }
            return pos;
        }
    }
});
define("editor/session", function (require, exports, module) {
    let Doc = require('../doc');
    let Selection = require('../selection');
    let Range = require('../range');
    module.exports = class Session {
        constructor() {
            this.doc = new Doc(`define("editor/fontMetrics", function (require, exports, module) {
    let dom = require('../lib/dom');
    module.exports = class FontMetrics {
\t\tconstructor(parent) {
            this.measureNode = document.buildElement({
                tag: "pre",
                style: {
                    position: "fixed",
                    left: 0,
                    top: 0,
                    opacity: 0,
                    pointerEvents: "none",
                    margin: 0,
                    padding: 0,
                    zIndex: -1
                },
                parent
            });
            this.sizes = {};
        }
        getCharaterWidth(char) {
            if (!this.sizes[char]) {
                this.measureNode.innerText = char;
                this.sizes[char] = this.measureNode.getBoundingClientRect().width;
            }
            return this.sizes[char];
        }
        getTextWdith(text) {
            let width = 0;
            for (let char of text) {
                width += this.getCharaterWidth(char);
            }
            return width;
        }
        setStyle(style) {
            dom.setStyle(this.measureNode, style);
            this.sizes = {};
        }
    }
});\n`.repeat(500));
            this.selection = new Selection(this);
        }
        insertText(text) {
            let lines = text.split("\n");
            let sel = this.selection;
            let doc = this.doc;
            this.removeSelected();

        }
        removeLeft() {

        }
        removeRight() {

        }
        removeSelected() {
            let sel = this.selection;
            let doc = this.doc;
            sel.ranges.forEach(range => {
                if (!range.isCollapsed()) {
                    let start = range.start;
                    let end = range.end;
                    doc.remove(start.row, start.column, end.row, endColumn);
                    moveRange(range, sel.ranges, "remove");
                    range.collapse();
                }
            });
        }
    }
});
define("editor/position", function (require, exports, module) {
    let Emitter = require('../emitter');

    function comparePoints(p1, p2) {
        let value = p1.row - p2.row || p1.column - p2.column;
        if (value < -1) {
            value = -1;
        }
        if (value > 1) {
            value = 1;
        }
        return value;
    }

    module.exports = class Position extends Emitter {
        constructor(row, column) {
            super();
            this.row = row;
            this.column = column;
            this.dir = "";
        }
        setPosition(row, column) {
            if (row == this.row && column == this.column) {
                this.dir = "";
                return;
            }
            let p1 = { row, column };
            let p2 = { row: this.row, column: this.column };
            let result = comparePoints(p1, p2);
            if (result < 0) {
                this.dir = "backward";
            } else if (result > 0) {
                this.dir = "forward";
            }
            let mutaion = {
                oldPos: {
                    row: this.row,
                    column: this.column
                },
                newPos: {
                    row,
                    column
                },
                target: this
            };
            this.row = row;
            this.column = column;
            this.emit("change", mutaion);
        }
    }
});
define("editor/range", function (require, exports, module) {
    let Emitter = require('../emitter');
    let Position = require('../position');
    let utils = require('../utils');

    class Range extends Emitter {
        constructor(startRow, startColumn, endRow, endColumn) {
            super();
            let $self = this;
            this.start = new Position(startRow, startColumn);
            this.end = new Position(endRow, endColumn);
            this._onPositionChanged = function () {
                if (this == $self.start || this == $self.end) {
                    $self.emit("change");
                }
            }
            this.start.on("change", this._onPositionChanged);
            this.end.on("change", this._onPositionChanged);
        }
        setStart(row, column) {
            this.start.setPosition(row, column);
        }
        setEnd(row, column) {
            this.end.setPosition(row, column);
        }
        moveTo(row, column) {
            this.setStart(row, column);
            this.setEnd(row, column);
        }
        isBackwards() {
            return utils.comparePoints(this.end, this.start) < 0;
        }
        collapseToStart() {
            this.end.setPosition(this.start.row, this.start.column);
        }
        collapseToEnd() {
            this.start.setPosition(this.end.row, this.end.column);
        }
        collapse() {
            if (this.isBackwards()) {
                this.collapseToEnd();
            } else {
                this.collapseToStart();
            }
        }
        toString() {
            return "start: " + this.start.row + " " + this.start.column +
                " end: " + this.end.row + " " + this.end.column;
        }
        isCollapsed() {
            return utils.comparePoints(this.start, this.end) == 0;
        }
        isMultiLine() {
            return Boolean(this.start.row - this.end.row);
        }
        forward() {
            return utils.setRangeForward(this);
        }
        backward() {
            return utils.setRangeBackward(this);
        }
        compare(point) {
            return utils.comparePoint(point, this);
        }
        destroy() {
            this.start.off("change", this._onPositionChanged);
            this.end.off("change", this._onPositionChanged);
            this.removeAllListeners();
        }
    }
    Range.comparePoints = utils.comparePoints;
    module.exports = Range;
});
define("editor/selection", function (require, exports, module) {
    let Emitter = require('../emitter');
    let Position = require('../position');
    let Range = require('../range');

    module.exports = class Selection extends Emitter {
        constructor(session) {
            super();
            this.session = session;
            this.doc = session.doc;
            this.ranges = [];
            this.anchor = new Position(0, 0);
            this.head = new Position(0, 0);
            this.MAX_RANGE_COUNT = 5000;

            let $self = this;
            this._onRangeUpdate = function () {
                if (this instanceof Range) {
                    $self.clipRangeToDocument(this);
                    $self.emit("change");
                }
            }
            this._onPositionUpdate = function () {
                if (this instanceof Position) {
                    let pos = $self.doc.clipPositionToDocument(this.row, this.column);
                    this.row = pos.row;
                    this.column = pos.column;
                }
            }
            this.anchor.on("change", this._onPositionUpdate);
            this.head.on("change", this._onPositionUpdate);

            this.on("change", function () {
                if (this.rangeCount() > this.MAX_RANGE_COUNT) {
                    this.limitRangeCount(this.MAX_RANGE_COUNT);
                }
            });
        }
        addRange(range) {
            range.on("change", this._onRangeUpdate);
            this.ranges.push(range);
            this.merge("forward");
            this.emit("change");
        }
        containsRange(range) {
            return this.indexOfRange(range) != -1;
        }
        indexOfRange(range) {
            return this.ranges.indexOf(range);
        }
        removeRange(range) {
            this.removeAt(this.indexOfRange(range));
        }
        removeAt(i) {
            if (this.getRangeAt(i) instanceof Range) {
                let removedRange = this.ranges.splice(i, 1)[0];
                removedRange.off("change", this._onRangeUpdate);
                this.emit("change");
                return removedRange;
            }
        }
        getRangeAt(i) {
            return this.ranges[i];
        }
        rangeCount() {
            return this.ranges.length;
        }
        merge(dir) {
            let rangeList = this.ranges.sort(Range.comparePoints);
            let result = [];
            for (let i = 0; ;) {
                let range = rangeList[i];
                let next = rangeList[i + 1];
                if (!range || !next) {
                    break;
                }
                let endPoint = range.isBackwards() ? range.start : range.end;
                let startPoint = next.isBackwards() ? next.end : next.start;
                if (Range.comparePoints(startPoint, endPoint) < 0) {
                    let end = next.isBackwards() ? next.start : next.end;
                    if (range.compare(end) > 0) {
                        if (range.isBackwards()) {
                            range.setStart(end.row, end.column);
                        } else {
                            range.setEnd(end.row, end.column);
                        }
                    }
                    switch (dir) {
                        case "forward":
                            range.forward();
                            break;
                        case "backward":
                            range.backward();
                            break;
                    }
                    this.removeRange(next);
                    result.push({ range, removed: next });
                } else {
                    ++i;
                    continue;
                }
            }
            return result;
        }
        clipRange(start, to) {
            let clipped = [];
            for (let i = 0; i < this.ranges.length; ++i) {
                let range = this.ranges[i];
                let isBack = range.isBackwards();
                let startPoint = isBack ? range.end : range.start;
                let endPoint = isBack ? range.start : range.end;
                if (endPoint.row < start || startPoint.row > to) {
                    continue;
                }
                let startRow = startPoint.row < start ? start : startPoint.row;
                let endRow = endPoint.row > to ? to : endPoint.row;
                let startColumn = startPoint.row < start ? 0 : startPoint.column;
                let endLine = this.session.doc.getLine(endPoint.row);
                let endColumn = endPoint.row > to ? endLine.length : endPoint.column;
                let clippedRange = new Range(startRow, startColumn, endRow, endColumn);
                clipped.push(clippedRange);
            }
            return clipped;
        }
        limitRangeCount(i) {
            while (this.rangeCount() && this.rangeCount() > i) {
                this.removeAt(this.rangeCount() - 1);
            }
        }
        clipRangeToDocument(range) {
            let start = this.doc.clipPositionToDocument(range.start);
            let end = this.doc.clipPositionToDocument(range.end);
            range.start.row = start.row;
            range.start.column = start.column;
            range.end.row = end.row;
            range.end.column = end.column;
            return range;
        }
        moveTo(row, column) {
            this.anchor.setPosition(row, column);
            this.moveSelectionHead(row, column);
        }
        moveSelectionRange(startRow, startColumn, endRow, endColumn) {
            this.moveSelectionAnchor(startRow, startColumn);
            this.moveSelectionHead(endRow, endColumn);
        }
        moveSelectionHead(row, column) {
            this.head.setPosition(row, column);
        }
        moveSelectionAnchor(row, column) {
            this.anchor.setPosition(row, column);
        }
        getRangesFromPoint(row, column) {
            let p = row instanceof Object ? row : { row, column };
            return this.ranges.filter(range => {
                return range.compare(p) == 0;
            });
        }
        isRangeOverPoint(point) {
            for (let i = 0; i < this.ranges.length; ++i) {
                let range = this.getRangeAt(i);
                if (range.compare(point) == 0) {
                    return true;
                }
            }
            return false;
        }
        select(startRow, startColumn, endRow, endColumn) {
            this.moveSelectionRange(startRow, startColumn, endRow, endColumn);
            let range = this.getRangeAt(0);
            if (!range) {
                range = new Range(0, 0, 0, 0);
                this.addRange(range);
            }
            range.setStart(startRow, startColumn);
            range.setEnd(endRow, endColumn);
            this.limitRangeCount(1);
        }
        selectTo(row, column) {
            this.select(this.anchor.row, this.anchor.column, row, column);
        }
        selectLeft() {
            this.ranges.forEach(range => {
                let endRow = range.end.row;
                let endColumn = range.end.column;
                if (endColumn <= 0) {
                    let prevRow = --endRow;
                    let line = this.doc.getLine(prevRow) || "";
                    endColumn = line.length;
                } else {
                    endColumn--;
                }
                range.setEnd(endRow, endColumn);
            });
            this.merge("backward");
        }
        selectRight() {
            this.ranges.forEach(range => {
                let endRow = range.end.row;
                let endColumn = range.end.column;
                let line = this.doc.getLine(endRow) || "";
                if (endColumn >= line.length) {
                    endColumn = 0;
                    endRow++;
                } else {
                    endColumn++;
                }
                range.setEnd(endRow, endColumn);
            });
            this.merge("forward");
        }
        selectUp() {
            this.ranges.forEach(range => {
                let endRow = range.end.row;
                let endColumn = this.head.column;
                let pos = this.doc.clipPositionToDocument(endRow - 1, endColumn);
                range.setEnd(pos.row, pos.column);
            });
            this.merge("backward");
        }
        selectDown() {
            this.ranges.forEach(range => {
                let endRow = range.end.row;
                let endColumn = this.head.column;
                let pos = this.doc.clipPositionToDocument(endRow + 1, endColumn);
                range.setEnd(pos.row, pos.column);
            });
            this.merge("forward");
        }
        selectAll() {
            this.select(0, 0, Infinity, Infinity);
        }
        moveCursorLeft() {
            this.ranges.forEach(range => {
                if (range.isCollapsed()) {
                    let row = range.start.row;
                    let column = range.start.column;
                    if (column <= 0) {
                        let prevRow = --row;
                        let preLine = this.doc.getLine(prevRow) || "";
                        column = preLine.length;
                    } else {
                        --column;
                    }
                    range.moveTo(row, column);
                } else {
                    if (range.isBackwards()) {
                        range.collapseToEnd();
                    } else {
                        range.collapseToStart();
                    }
                }
            });
            this.merge("backward");
        }
        moveCursorRight() {
            this.ranges.forEach(range => {
                if (range.isCollapsed()) {
                    let row = range.start.row;
                    let column = range.start.column;
                    let line = this.doc.getLine(row) || "";
                    if (column >= line.length) {
                        column = 0;
                        ++row;
                    } else {
                        ++column;
                    }
                    range.moveTo(row, column);
                } else {
                    if (range.isBackwards()) {
                        range.collapseToStart();
                    } else {
                        range.collapseToEnd();
                    }
                }
            });
            this.merge("forward");
        }
    }
});
define("editor", function (require, exports, module) {
    let Renderer = require("./renderer");
    let Session = require('./session');
    module.exports = class Editor {
        constructor(parent) {
            this.session = new Session();
            this.renderer = new Renderer(parent, this.session);
        }
    }
});
require("editor", ["require"], function (Editor, require) {
    require('./native_extend');
    window["Editor"] = Editor;
});
let editor = new Editor("#container");
console.log(editor);
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="container"></div>
</body>
</html>
* {
            margin: 0;
            padding: 0;
        }
        #container {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
        }
        .editor_container {
            position: relative;
            width: 100%;
            height: 100%;
            user-select: none;
        }
        .editor_container .gutter,
        .editor_container .scroller {
            position: absolute;
            top: 0;
            height: 100%;
            overflow: hidden;
        }
        .editor_container .gutter .gutter_layer {
            position: absolute;
            right: 10px;
            top: 0;
            text-align: right;
        }
        .editor_container .scroller {
            cursor: text;
        }
        .editor_container .scroller .content {
            position: absolute;
            left: 0;
            top: 0;
        }
        .editor_container .scrollbar .vsc {
            position: absolute;
            right: 0;
            top: 0;
            height: 100%;
            overflow: hidden;
            overflow-y: auto;
        }
        .editor_container .scrollbar .hsc {
            position: absolute;
            left: 0;
            bottom: 0;
            width: 100%;
            overflow: hidden;
            overflow-x: auto;
        }
        .editor_container .scroller .layer {
            position: absolute;
        }
        .editor_container  .marker .btlr {
            border-top-left-radius: 3px;
        }
        .editor_container  .marker .btrr {
            border-top-right-radius: 3px;
        }
        .editor_container  .marker .bbrr {
            border-bottom-right-radius: 3px;
        }
        .editor_container .marker .bblr {
            border-bottom-left-radius: 3px;
        }