UNPKG

@uiw/react-codemirror

Version:
1,007 lines (938 loc) 528 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("react"), require("react/jsx-runtime"), require("@codemirror/state"), require("@codemirror/theme-one-dark"), require("@codemirror/view")); else if(typeof define === 'function' && define.amd) define(["react", "react/jsx-runtime", , , ], factory); else if(typeof exports === 'object') exports["@uiw/codemirror"] = factory(require("react"), require("react/jsx-runtime"), require("@codemirror/state"), require("@codemirror/theme-one-dark"), require("@codemirror/view")); else root["@uiw/codemirror"] = factory(root["React"], root["ReactJSXRuntime"], root["CM"]["@codemirror/state"], root["CM"]["@codemirror/theme-one-dark"], root["CM"]["@codemirror/view"]); })(self, (__WEBPACK_EXTERNAL_MODULE__442__, __WEBPACK_EXTERNAL_MODULE__742__, __WEBPACK_EXTERNAL_MODULE__60__, __WEBPACK_EXTERNAL_MODULE__708__, __WEBPACK_EXTERNAL_MODULE__730__) => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 89 (__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ defaultLightThemeOption: () => (/* reexport safe */ _theme_light__WEBPACK_IMPORTED_MODULE_5__.c), /* harmony export */ getDefaultExtensions: () => (/* binding */ getDefaultExtensions) /* harmony export */ }); /* harmony import */ var _codemirror_commands__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(720); /* harmony import */ var _uiw_codemirror_extensions_basic_setup__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(687); /* harmony import */ var _codemirror_view__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(730); /* harmony import */ var _codemirror_view__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_codemirror_view__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _codemirror_theme_one_dark__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(708); /* harmony import */ var _codemirror_theme_one_dark__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_codemirror_theme_one_dark__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _codemirror_state__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(60); /* harmony import */ var _codemirror_state__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_codemirror_state__WEBPACK_IMPORTED_MODULE_4__); /* harmony import */ var _theme_light__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(806); /* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {}; /* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _codemirror_theme_one_dark__WEBPACK_IMPORTED_MODULE_3__) if(["default","getDefaultExtensions"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _codemirror_theme_one_dark__WEBPACK_IMPORTED_MODULE_3__[__WEBPACK_IMPORT_KEY__] /* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__); var getDefaultExtensions=function getDefaultExtensions(){var optios=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};var _optios$indentWithTab=optios.indentWithTab,defaultIndentWithTab=_optios$indentWithTab===void 0?true:_optios$indentWithTab,_optios$editable=optios.editable,editable=_optios$editable===void 0?true:_optios$editable,_optios$readOnly=optios.readOnly,readOnly=_optios$readOnly===void 0?false:_optios$readOnly,_optios$theme=optios.theme,theme=_optios$theme===void 0?'light':_optios$theme,_optios$placeholder=optios.placeholder,placeholderStr=_optios$placeholder===void 0?'':_optios$placeholder,_optios$basicSetup=optios.basicSetup,defaultBasicSetup=_optios$basicSetup===void 0?true:_optios$basicSetup;var getExtensions=[];if(defaultIndentWithTab){getExtensions.unshift(_codemirror_view__WEBPACK_IMPORTED_MODULE_2__.keymap.of([_codemirror_commands__WEBPACK_IMPORTED_MODULE_0__/* .indentWithTab */ .Yc]));}if(defaultBasicSetup){if(typeof defaultBasicSetup==='boolean'){getExtensions.unshift((0,_uiw_codemirror_extensions_basic_setup__WEBPACK_IMPORTED_MODULE_1__/* .basicSetup */ .o)());}else{getExtensions.unshift((0,_uiw_codemirror_extensions_basic_setup__WEBPACK_IMPORTED_MODULE_1__/* .basicSetup */ .o)(defaultBasicSetup));}}if(placeholderStr){getExtensions.unshift((0,_codemirror_view__WEBPACK_IMPORTED_MODULE_2__.placeholder)(placeholderStr));}switch(theme){case'light':getExtensions.push(_theme_light__WEBPACK_IMPORTED_MODULE_5__/* .defaultLightThemeOption */ .c);break;case'dark':getExtensions.push(_codemirror_theme_one_dark__WEBPACK_IMPORTED_MODULE_3__.oneDark);break;case'none':break;default:getExtensions.push(theme);break;}if(editable===false){getExtensions.push(_codemirror_view__WEBPACK_IMPORTED_MODULE_2__.EditorView.editable.of(false));}if(readOnly){getExtensions.push(_codemirror_state__WEBPACK_IMPORTED_MODULE_4__.EditorState.readOnly.of(true));}return[].concat(getExtensions);}; /***/ }, /***/ 806 (__unused_webpack_module, __webpack_exports__, __webpack_require__) { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ c: () => (/* binding */ defaultLightThemeOption) /* harmony export */ }); /* harmony import */ var _codemirror_view__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(730); /* harmony import */ var _codemirror_view__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_codemirror_view__WEBPACK_IMPORTED_MODULE_0__); var defaultLightThemeOption=_codemirror_view__WEBPACK_IMPORTED_MODULE_0__.EditorView.theme({'&':{backgroundColor:'#fff'}},{dark:false}); /***/ }, /***/ 341 (__unused_webpack_module, __webpack_exports__, __webpack_require__) { // EXPORTS __webpack_require__.d(__webpack_exports__, { Q: () => (/* binding */ ExternalChange), q: () => (/* binding */ useCodeMirror) }); ;// ../node_modules/@babel/runtime/helpers/esm/arrayLikeToArray.js function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } ;// ../node_modules/@babel/runtime/helpers/esm/arrayWithoutHoles.js function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } ;// ../node_modules/@babel/runtime/helpers/esm/iterableToArray.js function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } ;// ../node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } ;// ../node_modules/@babel/runtime/helpers/esm/nonIterableSpread.js function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } ;// ../node_modules/@babel/runtime/helpers/esm/toConsumableArray.js function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } ;// ../node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } ;// ../node_modules/@babel/runtime/helpers/esm/iterableToArrayLimit.js function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } ;// ../node_modules/@babel/runtime/helpers/esm/nonIterableRest.js function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } ;// ../node_modules/@babel/runtime/helpers/esm/slicedToArray.js function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } // EXTERNAL MODULE: external {"root":"React","commonjs2":"react","commonjs":"react","amd":"react"} var external_root_React_commonjs2_react_commonjs_react_amd_react_ = __webpack_require__(442); // EXTERNAL MODULE: external {"root":["CM","@codemirror/state"],"commonjs":"@codemirror/state","commonjs2":"@codemirror/state"} var state_ = __webpack_require__(60); // EXTERNAL MODULE: external {"root":["CM","@codemirror/view"],"commonjs":"@codemirror/view","commonjs2":"@codemirror/view"} var view_ = __webpack_require__(730); // EXTERNAL MODULE: ./src/getDefaultExtensions.ts var getDefaultExtensions = __webpack_require__(89); // EXTERNAL MODULE: ./src/utils.ts var utils = __webpack_require__(369); ;// ../node_modules/@babel/runtime/helpers/esm/classCallCheck.js function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } // EXTERNAL MODULE: ../node_modules/@babel/runtime/helpers/esm/toPropertyKey.js + 2 modules var toPropertyKey = __webpack_require__(236); ;// ../node_modules/@babel/runtime/helpers/esm/createClass.js function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, (0,toPropertyKey/* default */.A)(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } ;// ./src/timeoutLatch.ts // Setting / Unsetting timeouts for every keystroke was a significant overhead // Inspired from https://github.com/iostreamer-X/timeout-latch var TimeoutLatch=/*#__PURE__*/function(){function TimeoutLatch(callback,timeoutMS){_classCallCheck(this,TimeoutLatch);this.timeLeftMS=void 0;this.timeoutMS=void 0;this.isCancelled=false;this.isTimeExhausted=false;this.callbacks=[];this.timeLeftMS=timeoutMS;this.timeoutMS=timeoutMS;this.callbacks.push(callback);}return _createClass(TimeoutLatch,[{key:"tick",value:function tick(){if(!this.isCancelled&&!this.isTimeExhausted){this.timeLeftMS--;if(this.timeLeftMS<=0){this.isTimeExhausted=true;var callbacks=this.callbacks.slice();this.callbacks.length=0;callbacks.forEach(function(callback){try{callback();}catch(error){console.error('TimeoutLatch callback error:',error);}});}}}},{key:"cancel",value:function cancel(){this.isCancelled=true;this.callbacks.length=0;}},{key:"reset",value:function reset(){this.timeLeftMS=this.timeoutMS;this.isCancelled=false;this.isTimeExhausted=false;}},{key:"isDone",get:function get(){return this.isCancelled||this.isTimeExhausted;}}]);}();var Scheduler=/*#__PURE__*/function(){function Scheduler(){_classCallCheck(this,Scheduler);this.interval=null;this.latches=new Set();}return _createClass(Scheduler,[{key:"add",value:function add(latch){this.latches.add(latch);this.start();}},{key:"remove",value:function remove(latch){this.latches["delete"](latch);if(this.latches.size===0){this.stop();}}},{key:"start",value:function start(){var _this=this;if(this.interval===null){this.interval=setInterval(function(){_this.latches.forEach(function(latch){latch.tick();if(latch.isDone){_this.remove(latch);}});},1);}}},{key:"stop",value:function stop(){if(this.interval!==null){clearInterval(this.interval);this.interval=null;}}}]);}();var globalScheduler=null;var getScheduler=function getScheduler(){if(typeof window==='undefined'){return new Scheduler();}if(!globalScheduler){globalScheduler=new Scheduler();}return globalScheduler;}; ;// ./src/useCodeMirror.ts var ExternalChange=state_.Annotation.define();var TYPING_TIMOUT=200;// ms var emptyExtensions=[];function useCodeMirror(props){var value=props.value,selection=props.selection,onChange=props.onChange,onStatistics=props.onStatistics,onCreateEditor=props.onCreateEditor,onUpdate=props.onUpdate,_props$extensions=props.extensions,extensions=_props$extensions===void 0?emptyExtensions:_props$extensions,autoFocus=props.autoFocus,_props$theme=props.theme,theme=_props$theme===void 0?'light':_props$theme,_props$height=props.height,height=_props$height===void 0?null:_props$height,_props$minHeight=props.minHeight,minHeight=_props$minHeight===void 0?null:_props$minHeight,_props$maxHeight=props.maxHeight,maxHeight=_props$maxHeight===void 0?null:_props$maxHeight,_props$width=props.width,width=_props$width===void 0?null:_props$width,_props$minWidth=props.minWidth,minWidth=_props$minWidth===void 0?null:_props$minWidth,_props$maxWidth=props.maxWidth,maxWidth=_props$maxWidth===void 0?null:_props$maxWidth,_props$placeholder=props.placeholder,placeholderStr=_props$placeholder===void 0?'':_props$placeholder,_props$editable=props.editable,editable=_props$editable===void 0?true:_props$editable,_props$readOnly=props.readOnly,readOnly=_props$readOnly===void 0?false:_props$readOnly,_props$indentWithTab=props.indentWithTab,defaultIndentWithTab=_props$indentWithTab===void 0?true:_props$indentWithTab,_props$basicSetup=props.basicSetup,defaultBasicSetup=_props$basicSetup===void 0?true:_props$basicSetup,root=props.root,initialState=props.initialState;var _useState=(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(),_useState2=_slicedToArray(_useState,2),container=_useState2[0],setContainer=_useState2[1];var _useState3=(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(),_useState4=_slicedToArray(_useState3,2),view=_useState4[0],setView=_useState4[1];var _useState5=(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(),_useState6=_slicedToArray(_useState5,2),state=_useState6[0],setState=_useState6[1];var typingLatch=(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(function(){return{current:null};})[0];var pendingUpdate=(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(function(){return{current:null};})[0];var defaultThemeOption=view_.EditorView.theme({'&':{height:height,minHeight:minHeight,maxHeight:maxHeight,width:width,minWidth:minWidth,maxWidth:maxWidth},'& .cm-scroller':{height:'100% !important'}});var updateListener=view_.EditorView.updateListener.of(function(vu){if(vu.docChanged&&typeof onChange==='function'&&// Fix echoing of the remote changes: // If transaction is market as remote we don't have to call `onChange` handler again !vu.transactions.some(function(tr){return tr.annotation(ExternalChange);})){if(typingLatch.current){typingLatch.current.reset();}else{typingLatch.current=new TimeoutLatch(function(){if(pendingUpdate.current){var forceUpdate=pendingUpdate.current;pendingUpdate.current=null;forceUpdate();}typingLatch.current=null;},TYPING_TIMOUT);getScheduler().add(typingLatch.current);}var doc=vu.state.doc;var _value=doc.toString();onChange(_value,vu);}onStatistics&&onStatistics((0,utils/* getStatistics */.m)(vu));});var defaultExtensions=(0,getDefaultExtensions.getDefaultExtensions)({theme:theme,editable:editable,readOnly:readOnly,placeholder:placeholderStr,indentWithTab:defaultIndentWithTab,basicSetup:defaultBasicSetup});var getExtensions=[updateListener,defaultThemeOption].concat(_toConsumableArray(defaultExtensions));if(onUpdate&&typeof onUpdate==='function'){getExtensions.push(view_.EditorView.updateListener.of(onUpdate));}getExtensions=getExtensions.concat(extensions);(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useLayoutEffect)(function(){if(container&&!state){var config={doc:value,selection:selection,extensions:getExtensions};var stateCurrent=initialState?state_.EditorState.fromJSON(initialState.json,config,initialState.fields):state_.EditorState.create(config);setState(stateCurrent);if(!view){var viewCurrent=new view_.EditorView({state:stateCurrent,parent:container,root:root});setView(viewCurrent);onCreateEditor&&onCreateEditor(viewCurrent,stateCurrent);}}return function(){if(view){setState(undefined);setView(undefined);}};},[container,state]);(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function(){if(props.container){setContainer(props.container);}},[props.container]);(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function(){return function(){if(view){view.destroy();setView(undefined);}if(typingLatch.current){typingLatch.current.cancel();typingLatch.current=null;}};},[view]);(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function(){if(autoFocus&&view){view.focus();}},[autoFocus,view]);(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function(){if(view){view.dispatch({effects:state_.StateEffect.reconfigure.of(getExtensions)});}// eslint-disable-next-line react-hooks/exhaustive-deps },[theme,extensions,height,minHeight,maxHeight,width,minWidth,maxWidth,placeholderStr,editable,readOnly,defaultIndentWithTab,defaultBasicSetup,onChange,onUpdate]);(0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useEffect)(function(){if(value===undefined){return;}var currentValue=view?view.state.doc.toString():'';if(view&&value!==currentValue){var isTyping=typingLatch.current&&!typingLatch.current.isDone;var forceUpdate=function forceUpdate(){if(view&&value!==view.state.doc.toString()){view.dispatch({changes:{from:0,to:view.state.doc.toString().length,insert:value||''},annotations:[ExternalChange.of(true)]});}};if(!isTyping){forceUpdate();}else{pendingUpdate.current=forceUpdate;}}},[value,view]);return{state:state,setState:setState,view:view,setView:setView,container:container,setContainer:setContainer};} /***/ }, /***/ 369 (__unused_webpack_module, __webpack_exports__, __webpack_require__) { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ m: () => (/* binding */ getStatistics) /* harmony export */ }); var getStatistics=function getStatistics(view){return{line:view.state.doc.lineAt(view.state.selection.main.from),lineCount:view.state.doc.lines,lineBreak:view.state.lineBreak,length:view.state.doc.length,readOnly:view.state.readOnly,tabSize:view.state.tabSize,selection:view.state.selection,selectionAsSingle:view.state.selection.asSingle().main,ranges:view.state.selection.ranges,selectionCode:view.state.sliceDoc(view.state.selection.main.from,view.state.selection.main.to),selections:view.state.selection.ranges.map(function(r){return view.state.sliceDoc(r.from,r.to);}),selectedText:view.state.selection.ranges.some(function(r){return!r.empty;})};}; /***/ }, /***/ 442 (module) { module.exports = __WEBPACK_EXTERNAL_MODULE__442__; /***/ }, /***/ 742 (module) { module.exports = __WEBPACK_EXTERNAL_MODULE__742__; /***/ }, /***/ 60 (module) { module.exports = __WEBPACK_EXTERNAL_MODULE__60__; /***/ }, /***/ 708 (module) { module.exports = __WEBPACK_EXTERNAL_MODULE__708__; /***/ }, /***/ 730 (module) { module.exports = __WEBPACK_EXTERNAL_MODULE__730__; /***/ }, /***/ 687 (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) { // EXPORTS __webpack_require__.d(__webpack_exports__, { o: () => (/* binding */ basicSetup), V: () => (/* binding */ minimalSetup) }); // EXTERNAL MODULE: external {"root":["CM","@codemirror/view"],"commonjs":"@codemirror/view","commonjs2":"@codemirror/view"} var view_ = __webpack_require__(730); // EXTERNAL MODULE: external {"root":["CM","@codemirror/state"],"commonjs":"@codemirror/state","commonjs2":"@codemirror/state"} var state_ = __webpack_require__(60); // EXTERNAL MODULE: ../node_modules/@codemirror/commands/dist/index.js var dist = __webpack_require__(720); ;// ../node_modules/crelt/index.js function crelt() { var elt = arguments[0] if (typeof elt == "string") elt = document.createElement(elt) var i = 1, next = arguments[1] if (next && typeof next == "object" && next.nodeType == null && !Array.isArray(next)) { for (var name in next) if (Object.prototype.hasOwnProperty.call(next, name)) { var value = next[name] if (typeof value == "string") elt.setAttribute(name, value) else if (value != null) elt[name] = value } i++ } for (; i < arguments.length; i++) add(elt, arguments[i]) return elt } function add(elt, child) { if (typeof child == "string") { elt.appendChild(document.createTextNode(child)) } else if (child == null) { } else if (child.nodeType != null) { elt.appendChild(child) } else if (Array.isArray(child)) { for (var i = 0; i < child.length; i++) add(elt, child[i]) } else { throw new RangeError("Unsupported child node: " + child) } } ;// ../node_modules/@codemirror/search/dist/index.js const basicNormalize = typeof String.prototype.normalize == "function" ? x => x.normalize("NFKD") : x => x; /** A search cursor provides an iterator over text matches in a document. */ class SearchCursor { /** Create a text cursor. The query is the search string, `from` to `to` provides the region to search. When `normalize` is given, it will be called, on both the query string and the content it is matched against, before comparing. You can, for example, create a case-insensitive search by passing `s => s.toLowerCase()`. Text is always normalized with [`.normalize("NFKD")`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize) (when supported). */ constructor(text, query, from = 0, to = text.length, normalize, test) { this.test = test; /** The current match (only holds a meaningful value after [`next`](https://codemirror.net/6/docs/ref/#search.SearchCursor.next) has been called and when `done` is false). */ this.value = { from: 0, to: 0 }; /** Whether the end of the iterated region has been reached. */ this.done = false; this.matches = []; this.buffer = ""; this.bufferPos = 0; this.iter = text.iterRange(from, to); this.bufferStart = from; this.normalize = normalize ? x => normalize(basicNormalize(x)) : basicNormalize; this.query = this.normalize(query); } peek() { if (this.bufferPos == this.buffer.length) { this.bufferStart += this.buffer.length; this.iter.next(); if (this.iter.done) return -1; this.bufferPos = 0; this.buffer = this.iter.value; } return (0,state_.codePointAt)(this.buffer, this.bufferPos); } /** Look for the next match. Updates the iterator's [`value`](https://codemirror.net/6/docs/ref/#search.SearchCursor.value) and [`done`](https://codemirror.net/6/docs/ref/#search.SearchCursor.done) properties. Should be called at least once before using the cursor. */ next() { while (this.matches.length) this.matches.pop(); return this.nextOverlapping(); } /** The `next` method will ignore matches that partially overlap a previous match. This method behaves like `next`, but includes such matches. */ nextOverlapping() { for (;;) { let next = this.peek(); if (next < 0) { this.done = true; return this; } let str = (0,state_.fromCodePoint)(next), start = this.bufferStart + this.bufferPos; this.bufferPos += (0,state_.codePointSize)(next); let norm = this.normalize(str); if (norm.length) for (let i = 0, pos = start;; i++) { let code = norm.charCodeAt(i); let match = this.match(code, pos, this.bufferPos + this.bufferStart); if (i == norm.length - 1) { if (match) { this.value = match; return this; } break; } if (pos == start && i < str.length && str.charCodeAt(i) == code) pos++; } } } match(code, pos, end) { let match = null; for (let i = 0; i < this.matches.length; i += 2) { let index = this.matches[i], keep = false; if (this.query.charCodeAt(index) == code) { if (index == this.query.length - 1) { match = { from: this.matches[i + 1], to: end }; } else { this.matches[i]++; keep = true; } } if (!keep) { this.matches.splice(i, 2); i -= 2; } } if (this.query.charCodeAt(0) == code) { if (this.query.length == 1) match = { from: pos, to: end }; else this.matches.push(1, pos); } if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferStart)) match = null; return match; } } if (typeof Symbol != "undefined") SearchCursor.prototype[Symbol.iterator] = function () { return this; }; const empty = { from: -1, to: -1, match: /*@__PURE__*//.*/.exec("") }; const baseFlags = "gm" + (/x/.unicode == null ? "" : "u"); /** This class is similar to [`SearchCursor`](https://codemirror.net/6/docs/ref/#search.SearchCursor) but searches for a regular expression pattern instead of a plain string. */ class RegExpCursor { /** Create a cursor that will search the given range in the given document. `query` should be the raw pattern (as you'd pass it to `new RegExp`). */ constructor(text, query, options, from = 0, to = text.length) { this.text = text; this.to = to; this.curLine = ""; /** Set to `true` when the cursor has reached the end of the search range. */ this.done = false; /** Will contain an object with the extent of the match and the match object when [`next`](https://codemirror.net/6/docs/ref/#search.RegExpCursor.next) sucessfully finds a match. */ this.value = empty; if (/\\[sWDnr]|\n|\r|\[\^/.test(query)) return new MultilineRegExpCursor(text, query, options, from, to); this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : "")); this.test = options === null || options === void 0 ? void 0 : options.test; this.iter = text.iter(); let startLine = text.lineAt(from); this.curLineStart = startLine.from; this.matchPos = toCharEnd(text, from); this.getLine(this.curLineStart); } getLine(skip) { this.iter.next(skip); if (this.iter.lineBreak) { this.curLine = ""; } else { this.curLine = this.iter.value; if (this.curLineStart + this.curLine.length > this.to) this.curLine = this.curLine.slice(0, this.to - this.curLineStart); this.iter.next(); } } nextLine() { this.curLineStart = this.curLineStart + this.curLine.length + 1; if (this.curLineStart > this.to) this.curLine = ""; else this.getLine(0); } /** Move to the next match, if there is one. */ next() { for (let off = this.matchPos - this.curLineStart;;) { this.re.lastIndex = off; let match = this.matchPos <= this.to && this.re.exec(this.curLine); if (match) { let from = this.curLineStart + match.index, to = from + match[0].length; this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0)); if (from == this.curLineStart + this.curLine.length) this.nextLine(); if ((from < to || from > this.value.to) && (!this.test || this.test(from, to, match))) { this.value = { from, to, match }; return this; } off = this.matchPos - this.curLineStart; } else if (this.curLineStart + this.curLine.length < this.to) { this.nextLine(); off = 0; } else { this.done = true; return this; } } } } const flattened = /*@__PURE__*/new WeakMap(); // Reusable (partially) flattened document strings class FlattenedDoc { constructor(from, text) { this.from = from; this.text = text; } get to() { return this.from + this.text.length; } static get(doc, from, to) { let cached = flattened.get(doc); if (!cached || cached.from >= to || cached.to <= from) { let flat = new FlattenedDoc(from, doc.sliceString(from, to)); flattened.set(doc, flat); return flat; } if (cached.from == from && cached.to == to) return cached; let { text, from: cachedFrom } = cached; if (cachedFrom > from) { text = doc.sliceString(from, cachedFrom) + text; cachedFrom = from; } if (cached.to < to) text += doc.sliceString(cached.to, to); flattened.set(doc, new FlattenedDoc(cachedFrom, text)); return new FlattenedDoc(from, text.slice(from - cachedFrom, to - cachedFrom)); } } class MultilineRegExpCursor { constructor(text, query, options, from, to) { this.text = text; this.to = to; this.done = false; this.value = empty; this.matchPos = toCharEnd(text, from); this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : "")); this.test = options === null || options === void 0 ? void 0 : options.test; this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Chunk.Base */)); } chunkEnd(pos) { return pos >= this.to ? this.to : this.text.lineAt(pos).to; } next() { for (;;) { let off = this.re.lastIndex = this.matchPos - this.flat.from; let match = this.re.exec(this.flat.text); // Skip empty matches directly after the last match if (match && !match[0] && match.index == off) { this.re.lastIndex = off + 1; match = this.re.exec(this.flat.text); } if (match) { let from = this.flat.from + match.index, to = from + match[0].length; // If a match goes almost to the end of a noncomplete chunk, try // again, since it'll likely be able to match more if ((this.flat.to >= this.to || match.index + match[0].length <= this.flat.text.length - 10) && (!this.test || this.test(from, to, match))) { this.value = { from, to, match }; this.matchPos = toCharEnd(this.text, to + (from == to ? 1 : 0)); return this; } } if (this.flat.to == this.to) { this.done = true; return this; } // Grow the flattened doc this.flat = FlattenedDoc.get(this.text, this.flat.from, this.chunkEnd(this.flat.from + this.flat.text.length * 2)); } } } if (typeof Symbol != "undefined") { RegExpCursor.prototype[Symbol.iterator] = MultilineRegExpCursor.prototype[Symbol.iterator] = function () { return this; }; } function validRegExp(source) { try { new RegExp(source, baseFlags); return true; } catch (_a) { return false; } } function toCharEnd(text, pos) { if (pos >= text.length) return pos; let line = text.lineAt(pos), next; while (pos < line.to && (next = line.text.charCodeAt(pos - line.from)) >= 0xDC00 && next < 0xE000) pos++; return pos; } /** Command that shows a dialog asking the user for a line number, and when a valid position is provided, moves the cursor to that line. Supports line numbers, relative line offsets prefixed with `+` or `-`, document percentages suffixed with `%`, and an optional column position by adding `:` and a second number after the line number. */ const gotoLine = view => { let { state } = view; let line = String(state.doc.lineAt(view.state.selection.main.head).number); let { close, result } = (0,view_.showDialog)(view, { label: state.phrase("Go to line"), input: { type: "text", name: "line", value: line }, focus: true, submitLabel: state.phrase("go"), }); result.then(form => { let match = form && /^([+-])?(\d+)?(:\d+)?(%)?$/.exec(form.elements["line"].value); if (!match) { view.dispatch({ effects: close }); return; } let startLine = state.doc.lineAt(state.selection.main.head); let [, sign, ln, cl, percent] = match; let col = cl ? +cl.slice(1) : 0; let line = ln ? +ln : startLine.number; if (ln && percent) { let pc = line / 100; if (sign) pc = pc * (sign == "-" ? -1 : 1) + (startLine.number / state.doc.lines); line = Math.round(state.doc.lines * pc); } else if (ln && sign) { line = line * (sign == "-" ? -1 : 1) + startLine.number; } let docLine = state.doc.line(Math.max(1, Math.min(state.doc.lines, line))); let selection = state_.EditorSelection.cursor(docLine.from + Math.max(0, Math.min(col, docLine.length))); view.dispatch({ effects: [close, view_.EditorView.scrollIntoView(selection.from, { y: 'center' })], selection, }); }); return true; }; const defaultHighlightOptions = { highlightWordAroundCursor: false, minSelectionLength: 1, maxMatches: 100, wholeWords: false }; const highlightConfig = /*@__PURE__*/state_.Facet.define({ combine(options) { return (0,state_.combineConfig)(options, defaultHighlightOptions, { highlightWordAroundCursor: (a, b) => a || b, minSelectionLength: Math.min, maxMatches: Math.min }); } }); /** This extension highlights text that matches the selection. It uses the `"cm-selectionMatch"` class for the highlighting. When `highlightWordAroundCursor` is enabled, the word at the cursor itself will be highlighted with `"cm-selectionMatch-main"`. */ function highlightSelectionMatches(options) { let ext = [defaultTheme, matchHighlighter]; if (options) ext.push(highlightConfig.of(options)); return ext; } const matchDeco = /*@__PURE__*/view_.Decoration.mark({ class: "cm-selectionMatch" }); const mainMatchDeco = /*@__PURE__*/view_.Decoration.mark({ class: "cm-selectionMatch cm-selectionMatch-main" }); // Whether the characters directly outside the given positions are non-word characters function insideWordBoundaries(check, state, from, to) { return (from == 0 || check(state.sliceDoc(from - 1, from)) != state_.CharCategory.Word) && (to == state.doc.length || check(state.sliceDoc(to, to + 1)) != state_.CharCategory.Word); } // Whether the characters directly at the given positions are word characters function insideWord(check, state, from, to) { return check(state.sliceDoc(from, from + 1)) == state_.CharCategory.Word && check(state.sliceDoc(to - 1, to)) == state_.CharCategory.Word; } const matchHighlighter = /*@__PURE__*/view_.ViewPlugin.fromClass(class { constructor(view) { this.decorations = this.getDeco(view); } update(update) { if (update.selectionSet || update.docChanged || update.viewportChanged) this.decorations = this.getDeco(update.view); } getDeco(view) { let conf = view.state.facet(highlightConfig); let { state } = view, sel = state.selection; if (sel.ranges.length > 1) return view_.Decoration.none; let range = sel.main, query, check = null; if (range.empty) { if (!conf.highlightWordAroundCursor) return view_.Decoration.none; let word = state.wordAt(range.head); if (!word) return view_.Decoration.none; check = state.charCategorizer(range.head); query = state.sliceDoc(word.from, word.to); } else { let len = range.to - range.from; if (len < conf.minSelectionLength || len > 200) return view_.Decoration.none; if (conf.wholeWords) { query = state.sliceDoc(range.from, range.to); // TODO: allow and include leading/trailing space? check = state.charCategorizer(range.head); if (!(insideWordBoundaries(check, state, range.from, range.to) && insideWord(check, state, range.from, range.to))) return view_.Decoration.none; } else { query = state.sliceDoc(range.from, range.to); if (!query) return view_.Decoration.none; } } let deco = []; for (let part of view.visibleRanges) { let cursor = new SearchCursor(state.doc, query, part.from, part.to); while (!cursor.next().done) { let { from, to } = cursor.value; if (!check || insideWordBoundaries(check, state, from, to)) { if (range.empty && from <= range.from && to >= range.to) deco.push(mainMatchDeco.range(from, to)); else if (from >= range.to || to <= range.from) deco.push(matchDeco.range(from, to)); if (deco.length > conf.maxMatches) return view_.Decoration.none; } } } return view_.Decoration.set(deco); } }, { decorations: v => v.decorations }); const defaultTheme = /*@__PURE__*/view_.EditorView.baseTheme({ ".cm-selectionMatch": { backgroundColor: "#99ff7780" }, ".cm-searchMatch .cm-selectionMatch": { backgroundColor: "transparent" } }); // Select the words around the cursors. const selectWord = ({ state, dispatch }) => { let { selection } = state; let newSel = state_.EditorSelection.create(selection.ranges.map(range => state.wordAt(range.head) || state_.EditorSelection.cursor(range.head)), selection.mainIndex); if (newSel.eq(selection)) return false; dispatch(state.update({ selection: newSel })); return true; }; // Find next occurrence of query relative to last cursor. Wrap around // the document if there are no more matches. function findNextOccurrence(state, query) { let { main, ranges } = state.selection; let word = state.wordAt(main.head), fullWord = word && word.from == main.from && word.to == main.to; for (let cycled = false, cursor = new SearchCursor(state.doc, query, ranges[ranges.length - 1].to);;) { cursor.next(); if (cursor.done) { if (cycled) return null; cursor = new SearchCursor(state.doc, query, 0, Math.max(0, ranges[ranges.length - 1].from - 1)); cycled = true; } else { if (cycled && ranges.some(r => r.from == cursor.value.from)) continue; if (fullWord) { let word = state.wordAt(cursor.value.from); if (!word || word.from != cursor.value.from || word.to != cursor.value.to) continue; } return cursor.value; } } } /** Select next occurrence of the current selection. Expand selection to the surrounding word when the selection is empty. */ const selectNextOccurrence = ({ state, dispatch }) => { let { ranges } = state.selection; if (ranges.some(sel => sel.from === sel.to)) return selectWord({ state, dispatch }); let searchedText = state.sliceDoc(ranges[0].from, ranges[0].to); if (state.selection.ranges.some(r => state.sliceDoc(r.from, r.to) != searchedText)) return false; let range = findNextOccurrence(state, searchedText); if (!range) return false; dispatch(state.update({ selection: state.selection.addRange(state_.EditorSelection.range(range.from, range.to), false), effects: view_.EditorView.scrollIntoView(range.to) })); return true; }; const searchConfigFacet = /*@__PURE__*/state_.Facet.define({ combine(configs) { return (0,state_.combineConfig)(configs, { top: false, caseSensitive: false, literal: false, regexp: false, wholeWord: false, createPanel: view => new SearchPanel(view), scrollToMatch: range => view_.EditorView.scrollIntoView(range) }); } }); /** Add search state to the editor configuration, and optionally configure the search extension. ([`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel) will automatically enable this if it isn't already on). */ function search(config) { return config ? [searchConfigFacet.of(config), searchExtensions] : searchExtensions; } /** A search query. Part of the editor's search state. */ class SearchQuery { /** Create a query object. */ constructor(config) { this.search = config.search; this.caseSensitive = !!config.caseSensitive; this.literal = !!config.literal; this.regexp = !!config.regexp; this.replace = config.replace || ""; this.valid = !!this.search && (!this.regexp || validRegExp(this.search)); this.unquoted = this.unquote(this.search); this.wholeWord = !!config.wholeWord; this.test = config.test; } /** @internal */ unquote(text) { return this.literal ? text : text.replace(/\\([nrt\\])/g, (_, ch) => ch == "n" ? "\n" : ch == "r" ? "\r" : ch == "t" ? "\t" : "\\"); } /** Compare this query to another query. */ eq(other) { return this.search == other.search && this.replace == other.replace && this.caseSensitive == other.caseSensitive && this.regexp == other.regexp && this.wholeWord == other.wholeWord && this.test == other.test; } /** @internal */ create() { return this.regexp ? new RegExpQuery(this) : new StringQuery(this); } /** Get a search cursor for this query, searching through the given range in the given state. */ getCursor(state, from = 0, to) { let st = state.doc ? state : state_.EditorState.create({ doc: state }); if (to == null) to = st.doc.length; return this.regexp ? regexpCursor(this, st, from, to) : stringCursor(this, st, from, to); } } class QueryType { constructor(spec) { this.spec = spec; } } function wrapStringTest(test, state, inner) { return (from, to, buffer, bufferPos) => { if (inner && !inner(from, to, buffer, bufferPos)) return false; let match = from >= bufferPos && to <= bufferPos + buffer.length ? buffer.slice(from - bufferPos, to - bufferPos) : state.doc.sliceString(from, to); return test(match, state, from, to); }; } function stringCursor(spec, state, from, to) { let test; if (spec.wholeWord) test = stringWordTest(state.doc, state.charCategorizer(state.selection.main.head)); if (spec.test) test = wrapStringTest(spec.test, state, test); return new SearchCursor(state.doc, spec.unquoted, from, to, spec.caseSensitive ? undefined : x => x.toLowerCase(), test); } function stringWordTest(doc, categorizer) { return (from, to, buf, bufPos) => { if (bufPos > from || bufPos + buf.length < to) { bufPos = Math.max(0, from - 2); buf = doc.sliceString(bufPos, Math.min(doc.length, to + 2)); } return (categorizer(charBefore(buf, from - bufPos)) != state_.CharCategory.Word || categorizer(charAfter(buf, from - bufPos)) != state_.CharCategory.Word) && (categorizer(charAfter(buf, to - bufPos)) != state_.CharCategory.Word || categorizer(charBefore(buf, to - bufPos)) != state_.CharCategory.Word); }; } class StringQuery extends QueryType { constructor(spec) { super(spec); } nextMatch(state, curFrom, curTo) { let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping(); if (cursor.done) { let end = Math.min(state.doc.length, curFrom + this.spec.unquoted.length); cursor = stringCursor(this.spec, state, 0, end).nextOverlapping(); } return cursor.done || cursor.value.from == curFrom && cursor.value.to == curTo ? null : cursor.value; } // Searching in reverse is, rather than implementing an inverted search // cursor, done by scanning chunk after chunk forward. prevMatchInRange(state, from, to) { for (let pos = to;;) { let start = Math.max(from, pos - 10000 /* FindPrev.ChunkSize */ - this.spec.unquoted.length); let cursor = stringCursor(this.spec, state, start, pos), range = null; while (!cursor.nextOverlapping().done) range = cursor.value; if (range) return range; if (start == from) return null; pos -= 10000 /* FindPrev.ChunkSize */; } } prevMatch(state, curFrom, curTo) { let found = this.prevMatchInRange(state, 0, curFrom); if (!found) found = this.prevMatchInRange(state, Math.max(0, curTo - this.spec.unquoted.length), state.doc.length); return found && (found.from != curFrom || found.to != curTo) ? found : null; } getReplacement(_result) { return this.spec.unquote(this.spec.replace); } matchAll(state, limit) { let cursor = stringCursor(this.spec, state, 0, state.doc.length), ranges = []; while (!cursor.next().done) { if (ranges.length >= limit) return null; ranges.push(cursor.value); } return ranges; } highlight(state, from, to, add) { let cursor = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length)); while (!cursor.next().done) add(cursor.value.from, cursor.value.to); } } function wrapRegexpTest(test, state, inner) { return (from, to, match) => { return (!inner || inner(from, to, match)) && test(match[0], state, from, to); }; } function regexpCursor(spec, state, from, to) { let test; if (spec.wholeWord) test = regexpWordTest(state.charCategorizer(state.selection.main.head)); if (spec.test) test = wrapRegexpTest(spec.test, state, test); return new RegExpCursor(state.doc, spec.search, { ignoreCase: !spec.caseSensitive, test }, from, to); } function charBefore(str, index) { return str.slice((0,state_.findClusterBreak)(str, index, false), index); } function charAfter(str, index) { return str.slice(index, (0,state_.findClusterBreak)(str, index)); } function regexpWordTest(categorizer) { return (_from, _to, match) => !match[0].length || (categorizer(charBefore(match.input, match.index)) != state_.CharCategory.Word || categorizer(charAfter(match.input, match.index)) != state_.CharCategory.Word) && (categorizer(charAfter(match.input, match.index + match[0].length)) != state_.CharCategory.Word || categorizer(charBefore(match.input, match.index + match[0].length)) != state_.CharCategory.Word); } class RegExpQuery extends QueryType { nextMatch(state, curFrom, curTo) { let cursor = regexpCursor(this.spec, state, curTo, state.doc.length).next(); if (cursor.done) cursor = regexpCursor(this.spec, state, 0, curFrom).next(); return cursor.done ? null : cursor.value; } prevMatchInRange(state, from, to) { for (let size = 1;; size++) { let start = Math.max(from, to - size * 10000 /* FindPrev.ChunkSize */); let cursor = regexpCursor(this.spec, state, start, to), range = null; while (!cursor.next().done) range = cursor.value; if (range && (start == from || range.from > start + 10)) return range; if (start == from) return null; } } prevMatch(state, curFrom, curTo) { return this.prevMatchInRange(state, 0, curFrom) || this.prevMatchInRange(state, curTo, state.doc.length); } getReplacement(result) { return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g, (m, i) => { if