@actualwave/react-native-codeditor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +790 -0
- package/app.plugin.js +133 -0
- package/lib/module/BlockingView.js +25 -0
- package/lib/module/CodeEditor.js +239 -0
- package/lib/module/EditorAPI.js +2 -0
- package/lib/module/WebViewAPI.js +133 -0
- package/lib/module/index.js +6 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/app.plugin.d.ts +3 -0
- package/lib/typescript/babel.config.d.ts +10 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/react-native.config.d.ts +2 -0
- package/lib/typescript/scripts/copy-assets.d.ts +2 -0
- package/lib/typescript/src/BlockingView.d.ts +3 -0
- package/lib/typescript/src/CodeEditor.d.ts +33 -0
- package/lib/typescript/src/EditorAPI.d.ts +66 -0
- package/lib/typescript/src/WebViewAPI.d.ts +46 -0
- package/lib/typescript/src/index.d.ts +7 -0
- package/package.json +106 -0
- package/react-native.config.js +3 -0
- package/src/assets/codemirror/@actualwave_codemirror-lang-sksl.js +15 -0
- package/src/assets/codemirror/@babel_runtime_helpers_interopRequireDefault.js +1 -0
- package/src/assets/codemirror/@babel_runtime_helpers_objectSpread2.js +1 -0
- package/src/assets/codemirror/@babel_runtime_helpers_toConsumableArray.js +1 -0
- package/src/assets/codemirror/@codemirror_autocomplete.js +206 -0
- package/src/assets/codemirror/@codemirror_collab.js +31 -0
- package/src/assets/codemirror/@codemirror_commands.js +411 -0
- package/src/assets/codemirror/@codemirror_lang-angular.js +7 -0
- package/src/assets/codemirror/@codemirror_lang-cpp.js +7 -0
- package/src/assets/codemirror/@codemirror_lang-css.js +14 -0
- package/src/assets/codemirror/@codemirror_lang-go.js +13 -0
- package/src/assets/codemirror/@codemirror_lang-html.js +20 -0
- package/src/assets/codemirror/@codemirror_lang-java.js +7 -0
- package/src/assets/codemirror/@codemirror_lang-javascript.js +54 -0
- package/src/assets/codemirror/@codemirror_lang-jinja.js +15 -0
- package/src/assets/codemirror/@codemirror_lang-json.js +10 -0
- package/src/assets/codemirror/@codemirror_lang-less.js +10 -0
- package/src/assets/codemirror/@codemirror_lang-lezer.js +7 -0
- package/src/assets/codemirror/@codemirror_lang-liquid.js +15 -0
- package/src/assets/codemirror/@codemirror_lang-markdown.js +55 -0
- package/src/assets/codemirror/@codemirror_lang-php.js +7 -0
- package/src/assets/codemirror/@codemirror_lang-python.js +19 -0
- package/src/assets/codemirror/@codemirror_lang-rust.js +7 -0
- package/src/assets/codemirror/@codemirror_lang-sass.js +10 -0
- package/src/assets/codemirror/@codemirror_lang-sql.js +52 -0
- package/src/assets/codemirror/@codemirror_lang-vue.js +6 -0
- package/src/assets/codemirror/@codemirror_lang-wast.js +2 -0
- package/src/assets/codemirror/@codemirror_lang-xml.js +13 -0
- package/src/assets/codemirror/@codemirror_lang-yaml.js +13 -0
- package/src/assets/codemirror/@codemirror_language-data.js +5 -0
- package/src/assets/codemirror/@codemirror_language.js +524 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_apl.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_asciiarmor.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_asn1.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_asterisk.js +9 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_brainfuck.js +13 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_clike.js +31 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_clojure.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_cmake.js +6 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_cobol.js +7 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_coffeescript.js +14 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_commonlisp.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_crystal.js +18 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_css.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_cypher.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_d.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_diff.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_dockerfile.js +9 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_dtd.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_dylan.js +35 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_ebnf.js +9 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_ecl.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_eiffel.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_elm.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_erlang.js +52 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_factor.js +14 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_fcl.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_forth.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_fortran.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_gas.js +13 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_gherkin.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_go.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_groovy.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_haskell.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_haxe.js +12 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_http.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_idl.js +7 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_javascript.js +23 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_jinja2.js +8 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_julia.js +17 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_livescript.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_lua.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_mathematica.js +26 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_mbox.js +11 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_mirc.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_mllike.js +8 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_modelica.js +12 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_mscgen.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_mumps.js +16 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_nginx.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_nsis.js +20 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_ntriples.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_octave.js +8 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_oz.js +17 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_pascal.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_pegjs.js +25 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_perl.js +247 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_pig.js +6 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_powershell.js +7 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_properties.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_protobuf.js +7 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_pug.js +24 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_puppet.js +42 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_python.js +15 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_q.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_r.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_rpm.js +9 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_ruby.js +4 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_rust.js +5 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_sas.js +23 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_sass.js +26 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_scheme.js +14 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_shell.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_sieve.js +7 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_simple-mode.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_smalltalk.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_solr.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_sparql.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_spreadsheet.js +6 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_sql.js +84 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_stex.js +11 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_stylus.js +45 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_swift.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_tcl.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_textile.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_tiddlywiki.js +33 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_tiki.js +20 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_toml.js +4 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_troff.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_ttcn-cfg.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_ttcn.js +3 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_turtle.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_vb.js +11 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_vbscript.js +26 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_velocity.js +15 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_verilog.js +67 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_vhdl.js +2 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_wast.js +7 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_webidl.js +25 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_xml.js +6 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_xquery.js +51 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_yacas.js +16 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_yaml.js +1 -0
- package/src/assets/codemirror/@codemirror_legacy-modes_mode_z80.js +1 -0
- package/src/assets/codemirror/@codemirror_lint.js +46 -0
- package/src/assets/codemirror/@codemirror_merge.js +167 -0
- package/src/assets/codemirror/@codemirror_search.js +121 -0
- package/src/assets/codemirror/@codemirror_state.js +793 -0
- package/src/assets/codemirror/@codemirror_theme-one-dark.js +12 -0
- package/src/assets/codemirror/@codemirror_view.js +1210 -0
- package/src/assets/codemirror/@lezer_common.js +407 -0
- package/src/assets/codemirror/@lezer_cpp.js +4 -0
- package/src/assets/codemirror/@lezer_css.js +4 -0
- package/src/assets/codemirror/@lezer_go.js +3 -0
- package/src/assets/codemirror/@lezer_highlight.js +380 -0
- package/src/assets/codemirror/@lezer_html.js +20 -0
- package/src/assets/codemirror/@lezer_java.js +2 -0
- package/src/assets/codemirror/@lezer_javascript.js +7 -0
- package/src/assets/codemirror/@lezer_json.js +2 -0
- package/src/assets/codemirror/@lezer_lezer.js +2 -0
- package/src/assets/codemirror/@lezer_lr.js +325 -0
- package/src/assets/codemirror/@lezer_markdown.js +286 -0
- package/src/assets/codemirror/@lezer_php.js +3 -0
- package/src/assets/codemirror/@lezer_python.js +5 -0
- package/src/assets/codemirror/@lezer_rust.js +3 -0
- package/src/assets/codemirror/@lezer_sass.js +5 -0
- package/src/assets/codemirror/@lezer_xml.js +3 -0
- package/src/assets/codemirror/@lezer_yaml.js +12 -0
- package/src/assets/codemirror/@marijn_find-cluster-break.js +10 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-androidstudio.js +3 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-andromeda.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-atomone.js +6 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-aura.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-basic.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-bbedit.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-copilot.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-darcula.js +6 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-dracula.js +6 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-duotone.js +5 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-eclipse.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-github.js +3 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-material.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-monokai.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-nord.js +2 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-okaidia.js +2 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-solarized.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-sublime.js +2 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-tokyo-night.js +1 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-vscode.js +5 -0
- package/src/assets/codemirror/@uiw_codemirror-theme-xcode.js +3 -0
- package/src/assets/codemirror/@uiw_codemirror-themes.js +1 -0
- package/src/assets/codemirror/_core.js +4494 -0
- package/src/assets/codemirror/codemirror.js +46 -0
- package/src/assets/codemirror/crelt.js +1 -0
- package/src/assets/codemirror/style-mod.js +36 -0
- package/src/assets/codemirror/w3c-keyname.js +8 -0
- package/src/assets/codemirror-editor.umd.js +415 -0
- package/src/assets/editor.html +377 -0
- package/src/assets/webview-interface.umd.js +2 -0
|
@@ -0,0 +1,1210 @@
|
|
|
1
|
+
async function moduleInitFunction(requireAsyncModule,exports={}){const module={exports:exports};var require$$0=await requireAsyncModule("@codemirror/state"),require$$1=await requireAsyncModule("style-mod"),require$$2=await requireAsyncModule("w3c-keyname"),require$$3=await requireAsyncModule("crelt"),dist={},distExports=function requireDist(){return hasRequiredDist?dist:(hasRequiredDist=1,function(exports){function combineAttrs(source,target){for(let name in source)"class"==name&&target.class?target.class+=" "+source.class:"style"==name&&target.style?target.style+=";"+source.style:target[name]=source[name];return target}function attrsEq(a,b,ignore){if(a==b)return!0;a||(a=noAttrs),b||(b=noAttrs);let keysA=Object.keys(a),keysB=Object.keys(b);if(keysA.length-0!=keysB.length-0)return!1;for(let key of keysA)if(key!=ignore&&(-1==keysB.indexOf(key)||a[key]!==b[key]))return!1;return!0}function setAttrs(dom,attrs){for(let i=dom.attributes.length-1,name;0<=i;i--)name=dom.attributes[i].name,null==attrs[name]&&dom.removeAttribute(name);for(let name in attrs){let value=attrs[name];"style"==name?dom.style.cssText=value:dom.getAttribute(name)!=value&&dom.setAttribute(name,value)}}function updateAttrs(dom,prev,attrs){let changed=!1;if(prev)for(let name in prev)attrs&&name in attrs||(changed=!0,"style"==name?dom.style.cssText="":dom.removeAttribute(name));if(attrs)for(let name in attrs)prev&&prev[name]==attrs[name]||(changed=!0,"style"==name?dom.style.cssText=attrs[name]:dom.setAttribute(name,attrs[name]));return changed}function getAttrs(dom){let attrs=Object.create(null);for(let i=0,attr;i<dom.attributes.length;i++)attr=dom.attributes[i],attrs[attr.name]=attr.value;return attrs}/**
|
|
2
|
+
Widgets added to the content are described by subclasses of this
|
|
3
|
+
class. Using a description object like that makes it possible to
|
|
4
|
+
delay creating of the DOM structure for a widget until it is
|
|
5
|
+
needed, and to avoid redrawing widgets even if the decorations
|
|
6
|
+
that define them are recreated.
|
|
7
|
+
*/function getInclusive(spec,block=!1){let{inclusiveStart:start,inclusiveEnd:end}=spec;return null==start&&(start=spec.inclusive),null==end&&(end=spec.inclusive),{start:null!==start&&void 0!==start?start:block,end:null!==end&&void 0!==end?end:block}}function widgetsEq(a,b){return a==b||!!(a&&b&&a.compare(b))}function addRange(from,to,ranges,margin=0){let last=ranges.length-1;0<=last&&ranges[last]+margin>=from?ranges[last]=Math.max(ranges[last],to):ranges.push(from,to)}/**
|
|
8
|
+
A block wrapper defines a DOM node that wraps lines or other block
|
|
9
|
+
wrappers at the top of the document. It affects any line or block
|
|
10
|
+
widget that starts inside its range, including blocks starting
|
|
11
|
+
directly at `from` but not including `to`.
|
|
12
|
+
*/function getSelection(root){let target;// Browsers differ on whether shadow roots have a getSelection
|
|
13
|
+
// method. If it exists, use that, otherwise, call it on the
|
|
14
|
+
// document.
|
|
15
|
+
return target=11==root.nodeType?root.getSelection?root:root.ownerDocument:root,target.getSelection()}function contains(dom,node){return!!node&&(dom==node||dom.contains(1==node.nodeType?node:node.parentNode))}function hasSelection(dom,selection){if(!selection.anchorNode)return!1;try{// Firefox will raise 'permission denied' errors when accessing
|
|
16
|
+
// properties of `sel.anchorNode` when it's in a generated CSS
|
|
17
|
+
// element.
|
|
18
|
+
return contains(dom,selection.anchorNode)}catch(_){return!1}}function clientRectsFor(dom){return 3==dom.nodeType?textRange(dom,0,dom.nodeValue.length).getClientRects():1==dom.nodeType?dom.getClientRects():[]}// Scans forward and backward through DOM positions equivalent to the
|
|
19
|
+
// given one to see if the two are in the same place (i.e. after a
|
|
20
|
+
// text node vs at the end of that text node)
|
|
21
|
+
function isEquivalentPosition(node,off,targetNode,targetOff){return!!targetNode&&(scanFor(node,off,targetNode,targetOff,-1)||scanFor(node,off,targetNode,targetOff,1))}function domIndex(node){for(var index=0;;index++)if(node=node.previousSibling,!node)return index}function isBlockElement(node){return 1==node.nodeType&&/^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName)}function scanFor(node,off,targetNode,targetOff,dir){for(;;){if(node==targetNode&&off==targetOff)return!0;if(off==(0>dir?0:maxOffset(node))){if("DIV"==node.nodeName)return!1;let parent=node.parentNode;if(!parent||1!=parent.nodeType)return!1;off=domIndex(node)+(0>dir?0:1),node=parent}else if(1==node.nodeType){if(node=node.childNodes[off+(0>dir?-1:0)],1==node.nodeType&&"false"==node.contentEditable)return!1;off=0>dir?maxOffset(node):0}else return!1}}function maxOffset(node){return 3==node.nodeType?node.nodeValue.length:node.childNodes.length}function flattenRect(rect,left){let x=left?rect.left:rect.right;return{left:x,right:x,top:rect.top,bottom:rect.bottom}}function windowRect(win){let vp=win.visualViewport;return vp?{left:0,right:vp.width,top:0,bottom:vp.height}:{left:0,right:win.innerWidth,top:0,bottom:win.innerHeight}}function getScale(elt,rect){let scaleX=rect.width/elt.offsetWidth,scaleY=rect.height/elt.offsetHeight;return(.995<scaleX&&1.005>scaleX||!isFinite(scaleX)||1>Math.abs(rect.width-elt.offsetWidth))&&(scaleX=1),(.995<scaleY&&1.005>scaleY||!isFinite(scaleY)||1>Math.abs(rect.height-elt.offsetHeight))&&(scaleY=1),{scaleX,scaleY}}function scrollRectIntoView(dom,rect,side,x,y,xMargin,yMargin,ltr){let doc=dom.ownerDocument,win=doc.defaultView||window;for(let cur=dom,stop=!1;cur&&!stop;)if(1==cur.nodeType){// Element
|
|
22
|
+
let top=cur==doc.body,scaleX=1,scaleY=1,bounding;if(top)bounding=windowRect(win);else{if(/^(fixed|sticky)$/.test(getComputedStyle(cur).position)&&(stop=!0),cur.scrollHeight<=cur.clientHeight&&cur.scrollWidth<=cur.clientWidth){cur=cur.assignedSlot||cur.parentNode;continue}let rect=cur.getBoundingClientRect();({scaleX,scaleY}=getScale(cur,rect)),bounding={left:rect.left,right:rect.left+cur.clientWidth*scaleX,top:rect.top,bottom:rect.top+cur.clientHeight*scaleY}}let moveX=0,moveY=0;if("nearest"==y)rect.top<bounding.top+yMargin?(moveY=rect.top-(bounding.top+yMargin),0<side&&rect.bottom>bounding.bottom+moveY&&(moveY=rect.bottom-bounding.bottom+yMargin)):rect.bottom>bounding.bottom-yMargin&&(moveY=rect.bottom-bounding.bottom+yMargin,0>side&&rect.top-moveY<bounding.top&&(moveY=rect.top-(bounding.top+yMargin)));else{let rectHeight=rect.bottom-rect.top,boundingHeight=bounding.bottom-bounding.top,targetTop="center"==y&&rectHeight<=boundingHeight?rect.top+rectHeight/2-boundingHeight/2:"start"==y||"center"==y&&0>side?rect.top-yMargin:rect.bottom-boundingHeight+yMargin;moveY=targetTop-bounding.top}if("nearest"==x)rect.left<bounding.left+xMargin?(moveX=rect.left-(bounding.left+xMargin),0<side&&rect.right>bounding.right+moveX&&(moveX=rect.right-bounding.right+xMargin)):rect.right>bounding.right-xMargin&&(moveX=rect.right-bounding.right+xMargin,0>side&&rect.left<bounding.left+moveX&&(moveX=rect.left-(bounding.left+xMargin)));else{let targetLeft="center"==x?rect.left+(rect.right-rect.left)/2-(bounding.right-bounding.left)/2:"start"==x==ltr?rect.left-xMargin:rect.right-(bounding.right-bounding.left)+xMargin;moveX=targetLeft-bounding.left}if(moveX||moveY)if(top)win.scrollBy(moveX,moveY);else{let movedX=0,movedY=0;if(moveY){let start=cur.scrollTop;cur.scrollTop+=moveY/scaleY,movedY=(cur.scrollTop-start)*scaleY}if(moveX){let start=cur.scrollLeft;cur.scrollLeft+=moveX/scaleX,movedX=(cur.scrollLeft-start)*scaleX}rect={left:rect.left-movedX,top:rect.top-movedY,right:rect.right-movedX,bottom:rect.bottom-movedY},movedX&&1>Math.abs(movedX-moveX)&&(x="nearest"),movedY&&1>Math.abs(movedY-moveY)&&(y="nearest")}if(top)break;(rect.top<bounding.top||rect.bottom>bounding.bottom||rect.left<bounding.left||rect.right>bounding.right)&&(rect={left:Math.max(rect.left,bounding.left),right:Math.min(rect.right,bounding.right),top:Math.max(rect.top,bounding.top),bottom:Math.min(rect.bottom,bounding.bottom)}),cur=cur.assignedSlot||cur.parentNode}else if(11==cur.nodeType)// A shadow root
|
|
23
|
+
cur=cur.host;else break}function scrollableParents(dom,getX=!0){let doc=dom.ownerDocument,x=null,y=null;for(let cur=dom.parentNode;cur&&!(cur==doc.body||(!getX||x)&&y);)if(1==cur.nodeType)!y&&cur.scrollHeight>cur.clientHeight&&(y=cur),getX&&!x&&cur.scrollWidth>cur.clientWidth&&(x=cur),cur=cur.assignedSlot||cur.parentNode;else if(11==cur.nodeType)cur=cur.host;else break;return{x,y}}// Feature-detects support for .focus({preventScroll: true}), and uses
|
|
24
|
+
// a fallback kludge when not supported.
|
|
25
|
+
function focusPreventScroll(dom){if(dom.setActive)return dom.setActive();// in IE
|
|
26
|
+
if(preventScrollSupported)return dom.focus(preventScrollSupported);let stack=[];for(let cur=dom;cur&&(stack.push(cur,cur.scrollTop,cur.scrollLeft),cur!=cur.ownerDocument);cur=cur.parentNode);if(dom.focus(null==preventScrollSupported?{get preventScroll(){return preventScrollSupported={preventScroll:!0},!0}}:void 0),!preventScrollSupported){preventScrollSupported=!1;for(let i=0;i<stack.length;){let elt=stack[i++],top=stack[i++],left=stack[i++];elt.scrollTop!=top&&(elt.scrollTop=top),elt.scrollLeft!=left&&(elt.scrollLeft=left)}}}function textRange(node,from,to=from){let range=scratchRange||(scratchRange=document.createRange());return range.setEnd(node,to),range.setStart(node,from),range}function dispatchKey(elt,name,code,mods){let options={key:name,code:name,keyCode:code,which:code,cancelable:!0};mods&&({altKey:options.altKey,ctrlKey:options.ctrlKey,shiftKey:options.shiftKey,metaKey:options.metaKey}=mods);let down=new KeyboardEvent("keydown",options);down.synthetic=!0,elt.dispatchEvent(down);let up=new KeyboardEvent("keyup",options);return up.synthetic=!0,elt.dispatchEvent(up),down.defaultPrevented||up.defaultPrevented}function getRoot(node){for(;node;){if(node&&(9==node.nodeType||11==node.nodeType&&node.host))return node;node=node.assignedSlot||node.parentNode}return null}function atElementStart(doc,selection){let node=selection.focusNode,offset=selection.focusOffset;if(!node||selection.anchorNode!=node||selection.anchorOffset!=offset)return!1;// Safari can report bogus offsets (#1152)
|
|
27
|
+
for(offset=Math.min(offset,maxOffset(node));;)if(offset){if(1!=node.nodeType)return!1;let prev=node.childNodes[offset-1];"false"==prev.contentEditable?offset--:(node=prev,offset=maxOffset(node))}else{if(node==doc)return!0;offset=domIndex(node),node=node.parentNode}}function isScrolledToBottom(elt){return elt instanceof Window?elt.pageYOffset>Math.max(0,elt.document.documentElement.scrollHeight-elt.innerHeight-4):elt.scrollTop>Math.max(1,elt.scrollHeight-elt.clientHeight-4)}function textNodeBefore(startNode,startOffset){for(let node=startNode,offset=startOffset;;){if(3==node.nodeType&&0<offset)return{node:node,offset:offset};if(1==node.nodeType&&0<offset){if("false"==node.contentEditable)return null;node=node.childNodes[offset-1],offset=maxOffset(node)}else if(node.parentNode&&!isBlockElement(node))offset=domIndex(node),node=node.parentNode;else return null}}function textNodeAfter(startNode,startOffset){for(let node=startNode,offset=startOffset;;){if(3==node.nodeType&&offset<node.nodeValue.length)return{node:node,offset:offset};if(1==node.nodeType&&offset<node.childNodes.length){if("false"==node.contentEditable)return null;node=node.childNodes[offset],offset=0}else if(node.parentNode&&!isBlockElement(node))offset=domIndex(node)+1,node=node.parentNode;else return null}}// Decode a string with each type encoded as log2(type)
|
|
28
|
+
function dec(str){let result=[];for(let i=0;i<str.length;i++)result.push(1<<+str[i]);return result}// Character types for codepoints 0 to 0xf8
|
|
29
|
+
function charType(ch){return 247>=ch?LowTypes[ch]:1424<=ch&&1524>=ch?2/* T.R */:1536<=ch&&1785>=ch?ArabicTypes[ch-1536]:1774<=ch&&2220>=ch?4/* T.AL */:8192<=ch&&8204>=ch?256/* T.NI */:64336<=ch&&65023>=ch?4/* T.AL */:1/* T.L */}function isolatesEq(a,b){if(a.length!=b.length)return!1;for(let i=0;i<a.length;i++){let iA=a[i],iB=b[i];if(iA.from!=iB.from||iA.to!=iB.to||iA.direction!=iB.direction||!isolatesEq(iA.inner,iB.inner))return!1}return!0}// Reused array of character types
|
|
30
|
+
// Fill in the character types (in `types`) from `from` to `to` and
|
|
31
|
+
// apply W normalization rules.
|
|
32
|
+
function computeCharTypes(line,rFrom,rTo,isolates,outerType){for(let iI=0;iI<=isolates.length;iI++){let from=iI?isolates[iI-1].to:rFrom,to=iI<isolates.length?isolates[iI].from:rTo,prevType=iI?256/* T.NI */:outerType;// W1. Examine each non-spacing mark (NSM) in the level run, and
|
|
33
|
+
// change the type of the NSM to the type of the previous
|
|
34
|
+
// character. If the NSM is at the start of the level run, it will
|
|
35
|
+
// get the type of sor.
|
|
36
|
+
// W2. Search backwards from each instance of a European number
|
|
37
|
+
// until the first strong type (R, L, AL, or sor) is found. If an
|
|
38
|
+
// AL is found, change the type of the European number to Arabic
|
|
39
|
+
// number.
|
|
40
|
+
// W3. Change all ALs to R.
|
|
41
|
+
// (Left after this: L, R, EN, AN, ET, CS, NI)
|
|
42
|
+
for(let i=from,prev=prevType,prevStrong=prevType,type;i<to;i++)type=charType(line.charCodeAt(i)),512==type/* T.NSM */?type=prev:8==type/* T.EN */&&4==prevStrong/* T.AL */&&(type=16/* T.AN */),types[i]=4==type/* T.AL */?2/* T.R */:type,7&type/* T.Strong */&&(prevStrong=type),prev=type;// W5. A sequence of European terminators adjacent to European
|
|
43
|
+
// numbers changes to all European numbers.
|
|
44
|
+
// W6. Otherwise, separators and terminators change to Other
|
|
45
|
+
// Neutral.
|
|
46
|
+
// W7. Search backwards from each instance of a European number
|
|
47
|
+
// until the first strong type (R, L, or sor) is found. If an L is
|
|
48
|
+
// found, then change the type of the European number to L.
|
|
49
|
+
// (Left after this: L, R, EN+AN, NI)
|
|
50
|
+
for(let i=from,prev=prevType,prevStrong=prevType,type;i<to;i++){if(type=types[i],128==type/* T.CS */)i<to-1&&prev==types[i+1]&&24&prev/* T.Num */?type=types[i]=prev:types[i]=256/* T.NI */;else if(64==type/* T.ET */){let end=i+1;for(;end<to&&64==types[end]/* T.ET */;)end++;let replace=i&&8==prev/* T.EN */||end<rTo&&8==types[end]/* T.EN */?1==prevStrong/* T.L */?1/* T.L */:8/* T.EN */:256/* T.NI */;for(let j=i;j<end;j++)types[j]=replace;i=end-1}else 8==type/* T.EN */&&1==prevStrong/* T.L */&&(types[i]=1/* T.L */);prev=type,7&type/* T.Strong */&&(prevStrong=type)}}}// Process brackets throughout a run sequence.
|
|
51
|
+
function processBracketPairs(line,rFrom,rTo,isolates,outerType){let oppositeType=1==outerType/* T.L */?2/* T.R */:1/* T.L */;for(let iI=0,sI=0,context=0;iI<=isolates.length;iI++){let from=iI?isolates[iI-1].to:rFrom,to=iI<isolates.length?isolates[iI].from:rTo;// N0. Process bracket pairs in an isolating run sequence
|
|
52
|
+
// sequentially in the logical order of the text positions of the
|
|
53
|
+
// opening paired brackets using the logic given below. Within this
|
|
54
|
+
// scope, bidirectional types EN and AN are treated as R.
|
|
55
|
+
for(let i=from,ch,br,type;i<to;i++)// Keeps [startIndex, type, strongSeen] triples for each open
|
|
56
|
+
// bracket on BracketStack.
|
|
57
|
+
if(br=Brackets[ch=line.charCodeAt(i)]){if(0>br){// Closing bracket
|
|
58
|
+
for(let sJ=sI-3;0<=sJ;sJ-=3)if(BracketStack[sJ+1]==-br){let flags=BracketStack[sJ+2],type=2&flags/* Bracketed.EmbedInside */?outerType:4&flags/* Bracketed.OppositeInside */?1&flags/* Bracketed.OppositeBefore */?oppositeType:outerType:0;type&&(types[i]=types[BracketStack[sJ]]=type),sI=sJ;break}}else if(189==BracketStack.length/* Bracketed.MaxDepth */)break;else BracketStack[sI++]=i,BracketStack[sI++]=ch,BracketStack[sI++]=context;}else if(2==(type=types[i])/* T.R */||1==type/* T.L */){let embed=type==outerType;context=embed?0:1/* Bracketed.OppositeBefore */;for(let sJ=sI-3,cur;0<=sJ&&(cur=BracketStack[sJ+2],!(2&cur/* Bracketed.EmbedInside */));sJ-=3)if(embed)BracketStack[sJ+2]|=2/* Bracketed.EmbedInside */;else{if(4&cur/* Bracketed.OppositeInside */)break;BracketStack[sJ+2]|=4/* Bracketed.OppositeInside */}}}}function processNeutrals(rFrom,rTo,isolates,outerType){for(let iI=0,prev=outerType;iI<=isolates.length;iI++){let from=iI?isolates[iI-1].to:rFrom,to=iI<isolates.length?isolates[iI].from:rTo;// N1. A sequence of neutrals takes the direction of the
|
|
59
|
+
// surrounding strong text if the text on both sides has the same
|
|
60
|
+
// direction. European and Arabic numbers act as if they were R in
|
|
61
|
+
// terms of their influence on neutrals. Start-of-level-run (sor)
|
|
62
|
+
// and end-of-level-run (eor) are used at level run boundaries.
|
|
63
|
+
// N2. Any remaining neutrals take the embedding direction.
|
|
64
|
+
// (Left after this: L, R, EN+AN)
|
|
65
|
+
for(let i=from,type;i<to;)if(type=types[i],256==type/* T.NI */){let end=i+1;for(;;)if(end==to){if(iI==isolates.length)break;end=isolates[iI++].to,to=iI<isolates.length?isolates[iI].from:rTo}else if(256==types[end]/* T.NI */)end++;else break;let beforeL=1==prev/* T.L */,afterL=1==(end<rTo?types[end]:outerType)/* T.L */,replace=beforeL==afterL?beforeL?1/* T.L */:2/* T.R */:outerType;for(let j=end,jI=iI,fromJ=jI?isolates[jI-1].to:rFrom;j>i;)j==fromJ&&(j=isolates[--jI].from,fromJ=jI?isolates[jI-1].to:rFrom),types[--j]=replace;i=end}else prev=type,i++}}// Find the contiguous ranges of character types in a given range, and
|
|
66
|
+
// emit spans for them. Flip the order of the spans as appropriate
|
|
67
|
+
// based on the level, and call through to compute the spans for
|
|
68
|
+
// isolates at the proper point.
|
|
69
|
+
function emitSpans(line,from,to,level,baseLevel,isolates,order){let ourType=level%2?2/* T.R */:1/* T.L */;if(level%2==baseLevel%2)// Same dir as base direction, don't flip
|
|
70
|
+
for(let iCh=from,iI=0;iCh<to;){// Scan a section of characters in direction ourType, unless
|
|
71
|
+
// there's another type of char right after iCh, in which case
|
|
72
|
+
// we scan a section of other characters (which, if ourType ==
|
|
73
|
+
// T.L, may contain both T.R and T.AN chars).
|
|
74
|
+
let sameDir=!0,isNum=!1;if(iI==isolates.length||iCh<isolates[iI].from){let next=types[iCh];next!=ourType&&(sameDir=!1,isNum=16==next/* T.AN */)}// Holds an array of isolates to pass to a recursive call if we
|
|
75
|
+
// must recurse (to distinguish T.AN inside an RTL section in
|
|
76
|
+
// LTR text), null if we can emit directly
|
|
77
|
+
let recurse=sameDir||1!=ourType/* T.L */?null:[],localLevel=sameDir?level:level+1,iScan=iCh;run:for(;;)if(iI<isolates.length&&iScan==isolates[iI].from){if(isNum)break run;let iso=isolates[iI];// Scan ahead to verify that there is another char in this dir after the isolate(s)
|
|
78
|
+
if(!sameDir)for(let upto=iso.to,jI=iI+1;;){if(upto==to)break run;if(jI<isolates.length&&isolates[jI].from==upto)upto=isolates[jI++].to;else if(types[upto]==ourType)break run;else break}if(iI++,recurse)recurse.push(iso);else{iso.from>iCh&&order.push(new BidiSpan(iCh,iso.from,localLevel));let dirSwap=iso.direction==LTR!=!(localLevel%2);computeSectionOrder(line,dirSwap?level+1:level,baseLevel,iso.inner,iso.from,iso.to,order),iCh=iso.to}iScan=iso.to}else if(iScan==to||(sameDir?types[iScan]!=ourType:types[iScan]==ourType))break;else iScan++;recurse?emitSpans(line,iCh,iScan,level+1,baseLevel,recurse,order):iCh<iScan&&order.push(new BidiSpan(iCh,iScan,localLevel)),iCh=iScan}else// Iterate in reverse to flip the span order. Same code again, but
|
|
79
|
+
// going from the back of the section to the front
|
|
80
|
+
for(let iCh=to,iI=isolates.length;iCh>from;){let sameDir=!0,isNum=!1;if(!iI||iCh>isolates[iI-1].to){let next=types[iCh-1];next!=ourType&&(sameDir=!1,isNum=16==next/* T.AN */)}let recurse=sameDir||1!=ourType/* T.L */?null:[],localLevel=sameDir?level:level+1,iScan=iCh;run:for(;;)if(iI&&iScan==isolates[iI-1].to){if(isNum)break run;let iso=isolates[--iI];// Scan ahead to verify that there is another char in this dir after the isolate(s)
|
|
81
|
+
if(!sameDir)for(let upto=iso.from,jI=iI;;){if(upto==from)break run;if(jI&&isolates[jI-1].to==upto)upto=isolates[--jI].from;else if(types[upto-1]==ourType)break run;else break}if(recurse)recurse.push(iso);else{iso.to<iCh&&order.push(new BidiSpan(iso.to,iCh,localLevel));let dirSwap=iso.direction==LTR!=!(localLevel%2);computeSectionOrder(line,dirSwap?level+1:level,baseLevel,iso.inner,iso.from,iso.to,order),iCh=iso.from}iScan=iso.from}else if(iScan==from||(sameDir?types[iScan-1]!=ourType:types[iScan-1]==ourType))break;else iScan--;recurse?emitSpans(line,iScan,iCh,level+1,baseLevel,recurse,order):iScan<iCh&&order.push(new BidiSpan(iScan,iCh,localLevel)),iCh=iScan}}function computeSectionOrder(line,level,baseLevel,isolates,from,to,order){let outerType=level%2?2/* T.R */:1/* T.L */;computeCharTypes(line,from,to,isolates,outerType),processBracketPairs(line,from,to,isolates,outerType),processNeutrals(from,to,isolates,outerType),emitSpans(line,from,to,level,baseLevel,isolates,order)}function computeOrder(line,direction,isolates){if(!line)return[new BidiSpan(0,0,direction==RTL?1:0)];if(direction==LTR&&!isolates.length&&!BidiRE.test(line))return trivialOrder(line.length);if(isolates.length)for(;line.length>types.length;)types[types.length]=256/* T.NI */;// Make sure types array has no gaps
|
|
82
|
+
let order=[],level=direction==LTR?0:1;return computeSectionOrder(line,level,level,isolates,0,line.length,order),order}function trivialOrder(length){return[new BidiSpan(0,length,0)]}// This implementation moves strictly visually, without concern for a
|
|
83
|
+
// traversal visiting every logical position in the string. It will
|
|
84
|
+
// still do so for simple input, but situations like multiple isolates
|
|
85
|
+
// with the same level next to each other, or text going against the
|
|
86
|
+
// main dir at the end of the line, will make some positions
|
|
87
|
+
// unreachable with this motion. Each visible cursor position will
|
|
88
|
+
// correspond to the lower-level bidi span that touches it.
|
|
89
|
+
//
|
|
90
|
+
// The alternative would be to solve an order globally for a given
|
|
91
|
+
// line, making sure that it includes every position, but that would
|
|
92
|
+
// require associating non-canonical (higher bidi span level)
|
|
93
|
+
// positions with a given visual position, which is likely to confuse
|
|
94
|
+
// people. (And would generally be a lot more complicated.)
|
|
95
|
+
function moveVisually(line,order,dir,start,forward){var _a;let startIndex=start.head-line.from,spanI=BidiSpan.find(order,startIndex,null!==(_a=start.bidiLevel)&&void 0!==_a?_a:-1,start.assoc),span=order[spanI],spanEnd=span.side(forward,dir);// End of span
|
|
96
|
+
if(startIndex==spanEnd){let nextI=spanI+=forward?1:-1;if(0>nextI||nextI>=order.length)return null;span=order[spanI=nextI],startIndex=span.side(!forward,dir),spanEnd=span.side(forward,dir)}let nextIndex=state.findClusterBreak(line.text,startIndex,span.forward(forward,dir));(nextIndex<span.from||nextIndex>span.to)&&(nextIndex=spanEnd),movedOver=line.text.slice(Math.min(startIndex,nextIndex),Math.max(startIndex,nextIndex));let nextSpan=spanI==(forward?order.length-1:0)?null:order[spanI+(forward?1:-1)];return nextSpan&&nextIndex==spanEnd&&nextSpan.level+(forward?0:1)<span.level?state.EditorSelection.cursor(nextSpan.side(!forward,dir)+line.from,nextSpan.forward(forward,dir)?1:-1,nextSpan.level):state.EditorSelection.cursor(nextIndex+line.from,span.forward(forward,dir)?-1:1,span.level)}function autoDirection(text,from,to){for(let i=from,type;i<to;i++){if(type=charType(text.charCodeAt(i)),1==type/* T.L */)return LTR;if(2==type/* T.R */||4==type/* T.AL */)return RTL}return LTR}/**
|
|
97
|
+
Log or report an unhandled exception in client code. Should
|
|
98
|
+
probably only be used by extension code that allows client code to
|
|
99
|
+
provide functions, and calls those functions in a context where an
|
|
100
|
+
exception can't be propagated to calling code in a reasonable way
|
|
101
|
+
(for example when in an event handler).
|
|
102
|
+
|
|
103
|
+
Either calls a handler registered with
|
|
104
|
+
[`EditorView.exceptionSink`](https://codemirror.net/6/docs/ref/#view.EditorView^exceptionSink),
|
|
105
|
+
`window.onerror`, if defined, or `console.error` (in which case
|
|
106
|
+
it'll pass `context`, when given, as first argument).
|
|
107
|
+
*/function logException(state,exception,context){let handler=state.facet(exceptionSink);if(handler.length)handler[0](exception);else if(window.onerror&&window.onerror(exception+"",context,void 0,void 0,exception));else context?console.error(context+":",exception):console.error(exception)}function getIsolatedRanges(view,line){let isolates=view.state.facet(bidiIsolatedRanges);if(!isolates.length)return isolates;let sets=isolates.map(i=>i instanceof Function?i(view):i),result=[];return state.RangeSet.spans(sets,line.from,line.to,{point(){},span(fromDoc,toDoc,active,open){let from=fromDoc-line.from,to=toDoc-line.from,level=result;for(let i=active.length-1;0<=i;i--,open--){let direction=active[i].spec.bidiIsolate,update;if(null==direction&&(direction=autoDirection(line.text,from,to)),0<open&&level.length&&(update=level[level.length-1]).to==from&&update.direction==direction)update.to=to,level=update.inner;else{let add={from,to,direction,inner:[]};level.push(add),level=add.inner}}}}),result}function getScrollMargins(view){let left=0,right=0,top=0,bottom=0;for(let source of view.state.facet(scrollMargins)){let m=source(view);m&&(null!=m.left&&(left=Math.max(left,m.left)),null!=m.right&&(right=Math.max(right,m.right)),null!=m.top&&(top=Math.max(top,m.top)),null!=m.bottom&&(bottom=Math.max(bottom,m.bottom)))}return{left,right,top,bottom}}// Remove a DOM node and return its next sibling.
|
|
108
|
+
function rm$1(dom){let next=dom.nextSibling;return dom.parentNode.removeChild(dom),next}// The top-level tile. Its dom property equals view.contentDOM.
|
|
109
|
+
function fallbackRect(tile){let last=tile.dom.lastChild;if(!last)return tile.dom.getBoundingClientRect();let rects=clientRectsFor(last);return rects[rects.length-1]||null}function onSameLine(a,b){let posA=a.coordsIn(0,1),posB=b.coordsIn(0,1);return posA&&posB&&posB.top<posA.bottom}function hasContent(tile,requireText){let scan=tile=>{for(let ch of tile.children)if((requireText?ch.isText():ch.length)||scan(ch))return!0;return!1};return scan(tile)}function widgetFlags(deco){let flags=deco.isReplace?(0>deco.startSide?64/* TileFlag.IncStart */:0)|(0<deco.endSide?128/* TileFlag.IncEnd */:0):0<deco.startSide?32/* TileFlag.After */:16/* TileFlag.Before */;return deco.block&&(flags|=256/* TileFlag.Block */),flags}function addLineDeco(value,deco){let attrs=deco.spec.attributes,cls=deco.spec.class;return attrs||cls?(value||(value={class:"cm-line"}),attrs&&combineAttrs(attrs,value),cls&&(value.class+=" "+cls),value):value}function getMarks(ptr){let found=[];for(let i=ptr.parents.length,tile;1<i;i--)tile=i==ptr.parents.length?ptr.tile:ptr.parents[i].tile,tile instanceof MarkTile&&found.push(tile.mark);return found}function freeNode(node){let tile=Tile.get(node);return tile&&tile.setDOM(node.cloneNode()),node}function destroyDropped(tile,reused){let r=null===reused||void 0===reused?void 0:reused.get(tile);if(1!=r/* Reused.Full */){null==r&&tile.destroy();for(let ch of tile.children)destroyDropped(ch,reused)}}function betweenUneditable(pos){return 1==pos.node.nodeType&&pos.node.firstChild&&(0==pos.offset||"false"==pos.node.childNodes[pos.offset-1].contentEditable)&&(pos.offset==pos.node.childNodes.length||"false"==pos.node.childNodes[pos.offset].contentEditable)}function findCompositionNode(view,headPos){let sel=view.observer.selectionRange;if(!sel.focusNode)return null;let textBefore=textNodeBefore(sel.focusNode,sel.focusOffset),textAfter=textNodeAfter(sel.focusNode,sel.focusOffset),textNode=textBefore||textAfter;if(textAfter&&textBefore&&textAfter.node!=textBefore.node){let tileAfter=Tile.get(textAfter.node);if(!tileAfter||tileAfter.isText()&&tileAfter.text!=textAfter.node.nodeValue)textNode=textAfter;else if(view.docView.lastCompositionAfterCursor){let tileBefore=Tile.get(textBefore.node);!tileBefore||tileBefore.isText()&&tileBefore.text!=textBefore.node.nodeValue||(textNode=textAfter)}}if(view.docView.lastCompositionAfterCursor=textNode!=textBefore,!textNode)return null;let from=headPos-textNode.offset;return{from,to:from+textNode.node.nodeValue.length,node:textNode.node}}function findCompositionRange(view,changes,headPos){let found=findCompositionNode(view,headPos);if(!found)return null;let{node:textNode,from,to}=found,text=textNode.nodeValue;// Don't try to preserve multi-line compositions
|
|
110
|
+
if(/[\n\r]/.test(text))return null;if(view.state.doc.sliceString(found.from,found.to)!=text)return null;let inv=changes.invertedDesc;return{range:new ChangedRange(inv.mapPos(from),inv.mapPos(to),from,to),text:textNode}}function nextToUneditable(node,offset){return 1==node.nodeType?(offset&&"false"==node.childNodes[offset-1].contentEditable?1/* NextTo.Before */:0)|(offset<node.childNodes.length&&"false"==node.childNodes[offset].contentEditable?2/* NextTo.After */:0):0}function findChangedDeco(a,b,diff){let comp=new DecorationComparator$1;return state.RangeSet.compare(a,b,diff,comp),comp.changes}function findChangedWrappers(a,b,diff){let comp=new WrapperComparator;return state.RangeSet.compare(a,b,diff,comp),comp.changes}function inUneditable(node,inside){for(let cur=node;cur&&cur!=inside;cur=cur.assignedSlot||cur.parentNode)if(1==cur.nodeType&&"false"==cur.contentEditable)return!0;return!1}function touchesComposition(changes,composition){let touched=!1;return composition&&changes.iterChangedRanges((from,to)=>{from<composition.to&&to>composition.from&&(touched=!0)}),touched}function groupAt(state$1,pos,bias=1){let categorize=state$1.charCategorizer(pos),line=state$1.doc.lineAt(pos),linePos=pos-line.from;if(0==line.length)return state.EditorSelection.cursor(pos);0==linePos?bias=1:linePos==line.length&&(bias=-1);let from=linePos,to=linePos;0>bias?from=state.findClusterBreak(line.text,linePos,!1):to=state.findClusterBreak(line.text,linePos);let cat=categorize(line.text.slice(from,to));for(;0<from;){let prev=state.findClusterBreak(line.text,from,!1);if(categorize(line.text.slice(prev,from))!=cat)break;from=prev}for(;to<line.length;){let next=state.findClusterBreak(line.text,to);if(categorize(line.text.slice(to,next))!=cat)break;to=next}return state.EditorSelection.range(from+line.from,to+line.from)}function posAtCoordsImprecise(view,contentRect,block,x,y){let into=Math.round((x-contentRect.left)*view.defaultCharacterWidth);if(view.lineWrapping&&block.height>1.5*view.defaultLineHeight){let textHeight=view.viewState.heightOracle.textHeight,line=Math.floor((y-block.top-.5*(view.defaultLineHeight-textHeight))/textHeight);into+=line*view.viewState.heightOracle.lineLength}let content=view.state.sliceDoc(block.from,block.to);return block.from+state.findColumn(content,into,view.state.tabSize)}function blockAt(view,pos,side){let line=view.lineBlockAt(pos);if(Array.isArray(line.type)){let best;for(let l of line.type){if(l.from>pos)break;if(!(l.to<pos)){if(l.from<pos&&l.to>pos)return l;(!best||l.type==exports.BlockType.Text&&(best.type!=l.type||(0>side?l.from<pos:l.to>pos)))&&(best=l)}}return best||line}return line}function moveToLineBoundary(view,start,forward,includeWrap){let line=blockAt(view,start.head,start.assoc||-1),coords=includeWrap&&line.type==exports.BlockType.Text&&(view.lineWrapping||line.widgetLineBreaks)?view.coordsAtPos(0>start.assoc&&start.head>line.from?start.head-1:start.head):null;if(coords){let editorRect=view.dom.getBoundingClientRect(),direction=view.textDirectionAt(line.from),pos=view.posAtCoords({x:forward==(direction==exports.Direction.LTR)?editorRect.right-1:editorRect.left+1,y:(coords.top+coords.bottom)/2});if(null!=pos)return state.EditorSelection.cursor(pos,forward?-1:1)}return state.EditorSelection.cursor(forward?line.to:line.from,forward?-1:1)}function moveByChar(view,start,forward,by){let line=view.state.doc.lineAt(start.head),spans=view.bidiSpans(line),direction=view.textDirectionAt(line.from);for(let cur=start,check=null;;){let next=moveVisually(line,spans,direction,cur,forward),char=movedOver;if(!next){if(line.number==(forward?view.state.doc.lines:1))return cur;char="\n",line=view.state.doc.line(line.number+(forward?1:-1)),spans=view.bidiSpans(line),next=view.visualLineSide(line,!forward)}if(!check){if(!by)return next;check=by(char)}else if(!check(char))return cur;cur=next}}function byGroup(view,pos,start){let categorize=view.state.charCategorizer(pos),cat=categorize(start);return next=>{let nextCat=categorize(next);return cat==state.CharCategory.Space&&(cat=nextCat),cat==nextCat}}function moveVertically(view,start,forward,distance){let startPos=start.head,dir=forward?1:-1;if(startPos==(forward?view.state.doc.length:0))return state.EditorSelection.cursor(startPos,start.assoc);let goal=start.goalColumn,rect=view.contentDOM.getBoundingClientRect(),startCoords=view.coordsAtPos(startPos,start.assoc||((start.empty?forward:start.head==start.from)?1:-1)),docTop=view.documentTop,startY;if(startCoords)null==goal&&(goal=startCoords.left-rect.left),startY=0>dir?startCoords.top:startCoords.bottom;else{let line=view.viewState.lineBlockAt(startPos);null==goal&&(goal=Math.min(rect.right-rect.left,view.defaultCharacterWidth*(startPos-line.from))),startY=(0>dir?line.top:line.bottom)+docTop}let resolvedGoal=rect.left+goal,halfText=view.viewState.heightOracle.textHeight>>1,dist=null!==distance&&void 0!==distance?distance:halfText;for(let scan=0;;scan+=halfText){let y=startY+(dist+scan)*dir,pos=posAtCoords(view,{x:resolvedGoal,y},!1,dir);if(forward?y>rect.bottom:y<rect.top)return state.EditorSelection.cursor(pos.pos,pos.assoc);let posCoords=view.coordsAtPos(pos.pos,pos.assoc),mid=posCoords?(posCoords.top+posCoords.bottom)/2:0;if(!posCoords||(forward?mid>startY:mid<startY))return state.EditorSelection.cursor(pos.pos,pos.assoc,void 0,goal)}}function skipAtomicRanges(atoms,pos,bias){for(;;){let moved=0;for(let set of atoms)set.between(pos-1,pos+1,(from,to,value)=>{if(pos>from&&pos<to){let side=moved||bias||(pos-from<to-pos?-1:1);pos=0>side?from:to,moved=side}});if(!moved)return pos}}function skipAtomsForSelection(atoms,sel){let ranges=null;for(let i=0;i<sel.ranges.length;i++){let range=sel.ranges[i],updated=null;if(range.empty){let pos=skipAtomicRanges(atoms,range.from,0);pos!=range.from&&(updated=state.EditorSelection.cursor(pos,-1))}else{let from=skipAtomicRanges(atoms,range.from,-1),to=skipAtomicRanges(atoms,range.to,1);(from!=range.from||to!=range.to)&&(updated=state.EditorSelection.range(range.from==range.anchor?from:to,range.from==range.head?from:to))}updated&&(!ranges&&(ranges=sel.ranges.slice()),ranges[i]=updated)}return ranges?state.EditorSelection.create(ranges,sel.mainIndex):sel}function skipAtoms(view,oldPos,pos){let newPos=skipAtomicRanges(view.state.facet(atomicRanges).map(f=>f(view)),pos.from,oldPos.head>pos.from?-1:1);return newPos==pos.from?pos:state.EditorSelection.cursor(newPos,newPos<pos.from?1:-1)}function posAtCoords(view,coords,precise,scanY){let content=view.contentDOM.getBoundingClientRect(),docTop=content.top+view.viewState.paddingTop,{x,y}=coords,yOffset=y-docTop,block;// First find the block at the given Y position, if any. If scanY is
|
|
111
|
+
// given (used for vertical cursor motion), try to skip widgets and
|
|
112
|
+
// line padding.
|
|
113
|
+
for(;;){if(0>yOffset)return new PosAssoc(0,1);if(yOffset>view.viewState.docHeight)return new PosAssoc(view.state.doc.length,-1);if(block=view.elementAtHeight(yOffset),null==scanY)break;if(block.type==exports.BlockType.Text){if(0>scanY?block.to<view.viewport.from:block.from>view.viewport.to)break;// Check whether we aren't landing on the top/bottom padding of the line
|
|
114
|
+
let rect=view.docView.coordsAt(0>scanY?block.from:block.to,0<scanY?-1:1);if(rect&&(0>scanY?rect.top<=yOffset+docTop:rect.bottom>=yOffset+docTop))break}let halfLine=view.viewState.heightOracle.textHeight/2;yOffset=0<scanY?block.bottom+halfLine:block.top-halfLine}// If outside the viewport, return null if precise==true, an
|
|
115
|
+
// estimate otherwise.
|
|
116
|
+
if(view.viewport.from>=block.to||view.viewport.to<=block.from){if(precise)return null;if(block.type==exports.BlockType.Text){let pos=posAtCoordsImprecise(view,content,block,x,y);return new PosAssoc(pos,pos==block.from?1:-1)}}if(block.type!=exports.BlockType.Text)return yOffset<(block.top+block.bottom)/2?new PosAssoc(block.from,1):new PosAssoc(block.to,-1);// Here we know we're in a line, so run the logic for inline layout
|
|
117
|
+
let line=view.docView.lineAt(block.from,2);return line&&line.length==block.length||(line=view.docView.lineAt(block.from,-2)),new InlineCoordsScan(view,x,y,view.textDirectionAt(block.from)).scanTile(line,block.from)}function isAtEnd(parent,node,offset){for(;;){if(!node||offset<maxOffset(node))return!1;if(node==parent)return!0;offset=domIndex(node)+1,node=node.parentNode}}function isEmptyToEnd(node,end){let widgets;for(;;node=node.nextSibling){if(node==end||!node)break;let view=Tile.get(node);if(null===view||void 0===view||!view.isWidget())return!1;view&&(widgets||(widgets=[])).push(view)}if(widgets)for(let w of widgets){let override=w.overrideDOMText;if(null===override||void 0===override?void 0:override.length)return!1}return!0}function domBoundsAround(tile,from,to,offset){if(tile.isComposite()){let fromI=-1,fromStart=-1,toI=-1,toEnd=-1;for(let i=0,pos=offset,prevEnd=offset;i<tile.children.length;i++){let child=tile.children[i],end=pos+child.length;if(pos<from&&end>to)return domBoundsAround(child,from,to,pos);if(end>=from&&-1==fromI&&(fromI=i,fromStart=pos),pos>to&&child.dom.parentNode==tile.dom){toI=i,toEnd=prevEnd;break}prevEnd=end,pos=end+child.breakAfter}return{from:fromStart,to:0>toEnd?offset+tile.length:toEnd,startDOM:(fromI?tile.children[fromI-1].dom.nextSibling:null)||tile.dom.firstChild,endDOM:toI<tile.children.length&&0<=toI?tile.children[toI].dom:null}}return tile.isText()?{from:offset,to:offset+tile.length,startDOM:tile.dom,endDOM:tile.dom.nextSibling}:null}function applyDOMChange(view,domChange){let{newSel}=domChange,{state:state$1}=view,sel=state$1.selection.main,lastKey=view.inputState.lastKeyTime>Date.now()-100?view.inputState.lastKeyCode:-1,change;if(domChange.bounds){let{from,to}=domChange.bounds,preferredPos=sel.from,preferredSide=null;(8===lastKey||browser.android&&domChange.text.length<to-from)&&(preferredPos=sel.to,preferredSide="end");let cmp=state$1.doc.sliceString(from,to,LineBreakPlaceholder),selEnd,diff;!sel.empty&&sel.from>=from&&sel.to<=to&&(domChange.typeOver||cmp!=domChange.text)&&cmp.slice(0,sel.from-from)==domChange.text.slice(0,sel.from-from)&&cmp.slice(sel.to-from)==domChange.text.slice(selEnd=domChange.text.length-(cmp.length-(sel.to-from)))?change={from:sel.from,to:sel.to,insert:state.Text.of(domChange.text.slice(sel.from-from,selEnd).split(LineBreakPlaceholder))}:(diff=findDiff(cmp,domChange.text,preferredPos-from,preferredSide))&&(browser.chrome&&13==lastKey&&diff.toB==diff.from+2&&"\uFFFF\uFFFF"==domChange.text.slice(diff.from,diff.toB)&&diff.toB--,change={from:from+diff.from,to:from+diff.toA,insert:state.Text.of(domChange.text.slice(diff.from,diff.toB).split(LineBreakPlaceholder))})}else newSel&&(!view.hasFocus&&state$1.facet(editable)||sameSelPos(newSel,sel))&&(newSel=null);if(!change&&!newSel)return!1;if((browser.mac||browser.android)&&change&&change.from==change.to&&change.from==sel.head-1&&/^\. ?$/.test(change.insert.toString())&&"off"==view.contentDOM.getAttribute("autocorrect")?(newSel&&2==change.insert.length&&(newSel=state.EditorSelection.single(newSel.main.anchor-1,newSel.main.head-1)),change={from:change.from,to:change.to,insert:state.Text.of([change.insert.toString().replace("."," ")])}):state$1.doc.lineAt(sel.from).to<sel.to&&view.docView.lineHasWidget(sel.to)&&view.inputState.insertingTextAt>Date.now()-50?change={from:sel.from,to:sel.to,insert:state$1.toText(view.inputState.insertingText)}:browser.chrome&&change&&change.from==change.to&&change.from==sel.head&&"\n "==change.insert.toString()&&view.lineWrapping&&(newSel&&(newSel=state.EditorSelection.single(newSel.main.anchor-1,newSel.main.head-1)),change={from:sel.from,to:sel.to,insert:state.Text.of([" "])}),change)return applyDOMChangeInner(view,change,newSel,lastKey);if(newSel&&!sameSelPos(newSel,sel)){let scrollIntoView=!1,userEvent="select";return view.inputState.lastSelectionTime>Date.now()-50&&("select"==view.inputState.lastSelectionOrigin&&(scrollIntoView=!0),userEvent=view.inputState.lastSelectionOrigin,"select.pointer"==userEvent&&(newSel=skipAtomsForSelection(state$1.facet(atomicRanges).map(f=>f(view)),newSel))),view.dispatch({selection:newSel,scrollIntoView,userEvent}),!0}return!1}function applyDOMChangeInner(view,change,newSel,lastKey=-1){if(browser.ios&&view.inputState.flushIOSKey(change))return!0;let sel=view.state.selection.main;// Android browsers don't fire reasonable key events for enter,
|
|
118
|
+
// backspace, or delete. So this detects changes that look like
|
|
119
|
+
// they're caused by those keys, and reinterprets them as key
|
|
120
|
+
// events. (Some of these keys are also handled by beforeinput
|
|
121
|
+
// events and the pendingAndroidKey mechanism, but that's not
|
|
122
|
+
// reliable in all situations.)
|
|
123
|
+
if(browser.android&&(change.to==sel.to&&(// GBoard will sometimes remove a space it just inserted
|
|
124
|
+
// after a completion when you press enter
|
|
125
|
+
change.from==sel.from||change.from==sel.from-1&&" "==view.state.sliceDoc(change.from,sel.from))&&1==change.insert.length&&2==change.insert.lines&&dispatchKey(view.contentDOM,"Enter",13)||(change.from==sel.from-1&&change.to==sel.to&&0==change.insert.length||8==lastKey&&change.insert.length<change.to-change.from&&change.to>sel.head)&&dispatchKey(view.contentDOM,"Backspace",8)||change.from==sel.from&&change.to==sel.to+1&&0==change.insert.length&&dispatchKey(view.contentDOM,"Delete",46)))return!0;let text=change.insert.toString();0<=view.inputState.composing&&view.inputState.composing++;let defaultInsert=()=>defaultTr||(defaultTr=applyDefaultInsert(view,change,newSel)),defaultTr;return view.state.facet(inputHandler).some(h=>h(view,change.from,change.to,text,defaultInsert))||view.dispatch(defaultInsert()),!0}function applyDefaultInsert(view,change,newSel){let startState=view.state,sel=startState.selection.main,inAtomic=-1,tr;if(change.from==change.to&&change.from<sel.from||change.from>sel.to){let side=change.from<sel.from?-1:1,pos=0>side?sel.from:sel.to,moved=skipAtomicRanges(startState.facet(atomicRanges).map(f=>f(view)),pos,side);change.from==moved&&(inAtomic=moved)}if(-1<inAtomic)tr={changes:change,selection:state.EditorSelection.cursor(change.from+change.insert.length,-1)};else if(change.from>=sel.from&&change.to<=sel.to&&change.to-change.from>=(sel.to-sel.from)/3&&(!newSel||newSel.main.empty&&newSel.main.from==change.from+change.insert.length)&&0>view.inputState.composing){let before=sel.from<change.from?startState.sliceDoc(sel.from,change.from):"",after=sel.to>change.to?startState.sliceDoc(change.to,sel.to):"";tr=startState.replaceSelection(view.state.toText(before+change.insert.sliceString(0,void 0,view.state.lineBreak)+after))}else{let changes=startState.changes(change),mainSel=newSel&&newSel.main.to<=changes.newLength?newSel.main:void 0;// Try to apply a composition change to all cursors
|
|
126
|
+
if(1<startState.selection.ranges.length&&(0<=view.inputState.composing||view.inputState.compositionPendingChange)&&change.to<=sel.to+10&&change.to>=sel.to-10){let replaced=view.state.sliceDoc(change.from,change.to),composition=newSel&&findCompositionNode(view,newSel.main.head),compositionRange;if(composition){let dLen=change.insert.length-(change.to-change.from);compositionRange={from:composition.from,to:composition.to-dLen}}else compositionRange=view.state.doc.lineAt(sel.head);let offset=sel.to-change.to;tr=startState.changeByRange(range=>{if(range.from==sel.from&&range.to==sel.to)return{changes,range:mainSel||range.map(changes)};let to=range.to-offset,from=to-replaced.length;if(view.state.sliceDoc(from,to)!=replaced||// Unfortunately, there's no way to make multiple
|
|
127
|
+
// changes in the same node work without aborting
|
|
128
|
+
// composition, so cursors in the composition range are
|
|
129
|
+
// ignored.
|
|
130
|
+
to>=compositionRange.from&&from<=compositionRange.to)return{range};let rangeChanges=startState.changes({from,to,insert:change.insert}),selOff=range.to-sel.to;return{changes:rangeChanges,range:mainSel?state.EditorSelection.range(Math.max(0,mainSel.anchor+selOff),Math.max(0,mainSel.head+selOff)):range.map(rangeChanges)}})}else tr={changes,selection:mainSel&&startState.selection.replaceRange(mainSel)}}let userEvent="input.type";return(view.composing||view.inputState.compositionPendingChange&&view.inputState.compositionEndedAt>Date.now()-50)&&(view.inputState.compositionPendingChange=!1,userEvent+=".compose",view.inputState.compositionFirstChange&&(userEvent+=".start",view.inputState.compositionFirstChange=!1)),startState.update(tr,{userEvent,scrollIntoView:!0})}function findDiff(a,b,preferredPos,preferredSide){let minLen=Math.min(a.length,b.length),from=0;for(;from<minLen&&a.charCodeAt(from)==b.charCodeAt(from);)from++;if(from==minLen&&a.length==b.length)return null;let toA=a.length,toB=b.length;for(;0<toA&&0<toB&&a.charCodeAt(toA-1)==b.charCodeAt(toB-1);)toA--,toB--;if("end"==preferredSide){let adjust=Math.max(0,from-Math.min(toA,toB));preferredPos-=toA+adjust-from}if(toA<from&&a.length<b.length){let move=preferredPos<=from&&preferredPos>=toA?from-preferredPos:0;from-=move,toB=from+(toB-toA),toA=from}else if(toB<from){let move=preferredPos<=from&&preferredPos>=toB?from-preferredPos:0;from-=move,toA=from+(toA-toB),toB=from}return{from,toA,toB}}function selectionPoints(view){let result=[];if(view.root.activeElement!=view.contentDOM)return result;let{anchorNode,anchorOffset,focusNode,focusOffset}=view.observer.selectionRange;return anchorNode&&(result.push(new DOMPoint(anchorNode,anchorOffset)),(focusNode!=anchorNode||focusOffset!=anchorOffset)&&result.push(new DOMPoint(focusNode,focusOffset))),result}function selectionFromPoints(points,base){if(0==points.length)return null;let anchor=points[0].pos,head=2==points.length?points[1].pos:anchor;return-1<anchor&&-1<head?state.EditorSelection.single(anchor+base,head+base):null}function sameSelPos(selection,range){return range.head==selection.main.head&&range.anchor==selection.main.anchor}function bindHandler(plugin,handler){return(view,event)=>{try{return handler.call(plugin,event,view)}catch(e){logException(view.state,e)}}}function computeHandlers(plugins){function record(type){return result[type]||(result[type]={observers:[],handlers:[]})}let result=Object.create(null);for(let plugin of plugins){let spec=plugin.spec,handlers=spec&&spec.plugin.domEventHandlers,observers=spec&&spec.plugin.domEventObservers;if(handlers)for(let type in handlers){let f=handlers[type];f&&record(type).handlers.push(bindHandler(plugin.value,f))}if(observers)for(let type in observers){let f=observers[type];f&&record(type).observers.push(bindHandler(plugin.value,f))}}for(let type in handlers)record(type).handlers.push(handlers[type]);for(let type in observers)record(type).observers.push(observers[type]);return result}function dragScrollSpeed(dist){return .7*Math.max(0,dist)+8}function dist(a,b){return Math.max(Math.abs(a.clientX-b.clientX),Math.abs(a.clientY-b.clientY))}function addsSelectionRange(view,event){let facet=view.state.facet(clickAddsSelectionRange);return facet.length?facet[0](event):browser.mac?event.metaKey:event.ctrlKey}function dragMovesSelection(view,event){let facet=view.state.facet(dragMovesSelection$1);return facet.length?facet[0](event):browser.mac?!event.altKey:!event.ctrlKey}function isInPrimarySelection(view,event){let{main}=view.state.selection;if(main.empty)return!1;// On boundary clicks, check whether the coordinates are inside the
|
|
131
|
+
// selection's client rectangles
|
|
132
|
+
let sel=getSelection(view.root);if(!sel||0==sel.rangeCount)return!0;let rects=sel.getRangeAt(0).getClientRects();for(let i=0,rect;i<rects.length;i++)if(rect=rects[i],rect.left<=event.clientX&&rect.right>=event.clientX&&rect.top<=event.clientY&&rect.bottom>=event.clientY)return!0;return!1}function eventBelongsToEditor(view,event){if(!event.bubbles)return!0;if(event.defaultPrevented)return!1;for(let node=event.target,tile;node!=view.contentDOM;node=node.parentNode)if(!node||11==node.nodeType||(tile=Tile.get(node))&&tile.isWidget()&&!tile.isHidden&&tile.widget.ignoreEvent(event))return!1;return!0}function capturePaste(view){let parent=view.dom.parentNode;if(!parent)return;let target=parent.appendChild(document.createElement("textarea"));target.style.cssText="position: fixed; left: -10000px; top: 10px",target.focus(),setTimeout(()=>{view.focus(),target.remove(),doPaste(view,target.value)},50)}function textFilter(state,facet,text){for(let filter of state.facet(facet))text=filter(text,state);return text}function doPaste(view,input){input=textFilter(view.state,clipboardInputFilter,input);let{state:state$1}=view,i=1,text=state$1.toText(input),byLine=text.lines==state$1.selection.ranges.length,linewise=null!=lastLinewiseCopy&&state$1.selection.ranges.every(r=>r.empty)&&lastLinewiseCopy==text.toString(),changes;if(linewise){let lastLine=-1;changes=state$1.changeByRange(range=>{let line=state$1.doc.lineAt(range.from);if(line.from==lastLine)return{range};lastLine=line.from;let insert=state$1.toText((byLine?text.line(i++).text:input)+state$1.lineBreak);return{changes:{from:line.from,insert},range:state.EditorSelection.cursor(range.from+insert.length)}})}else changes=byLine?state$1.changeByRange(range=>{let line=text.line(i++);return{changes:{from:range.from,to:range.to,insert:line.text},range:state.EditorSelection.cursor(range.from+line.length)}}):state$1.replaceSelection(text);view.dispatch(changes,{userEvent:"input.paste",scrollIntoView:!0})}function rangeForClick(view,pos,bias,type){if(1==type)// Single click
|
|
133
|
+
return state.EditorSelection.cursor(pos,bias);if(2==type)// Double click
|
|
134
|
+
return groupAt(view.state,pos,bias);else{// Triple click
|
|
135
|
+
let visual=view.docView.lineAt(pos,bias),line=view.state.doc.lineAt(visual?visual.posAtEnd:pos),from=visual?visual.posAtStart:line.from,to=visual?visual.posAtEnd:line.to;return to<view.state.doc.length&&to==line.to&&to++,state.EditorSelection.range(from,to)}}function getClickType(event){if(!BadMouseDetail)return event.detail;let last=lastMouseDown,lastTime=lastMouseDownTime;return lastMouseDown=event,lastMouseDownTime=Date.now(),lastMouseDownCount=!last||lastTime>Date.now()-400&&2>Math.abs(last.clientX-event.clientX)&&2>Math.abs(last.clientY-event.clientY)?(lastMouseDownCount+1)%3:1}function basicMouseSelection(view,event){let start=view.posAndSideAtCoords({x:event.clientX,y:event.clientY},!1),type=getClickType(event),startSel=view.state.selection;return{update(update){update.docChanged&&(start.pos=update.changes.mapPos(start.pos),startSel=startSel.map(update.changes))},get(event,extend,multiple){let cur=view.posAndSideAtCoords({x:event.clientX,y:event.clientY},!1),range=rangeForClick(view,cur.pos,cur.assoc,type),removed;if(start.pos!=cur.pos&&!extend){let startRange=rangeForClick(view,start.pos,start.assoc,type),from=Math.min(startRange.from,range.from),to=Math.max(startRange.to,range.to);range=from<range.from?state.EditorSelection.range(from,to,range.assoc):state.EditorSelection.range(to,from,range.assoc)}return extend?startSel.replaceRange(startSel.main.extend(range.from,range.to,range.assoc)):multiple&&1==type&&1<startSel.ranges.length&&(removed=removeRangeAround(startSel,cur.pos))?removed:multiple?startSel.addRange(range):state.EditorSelection.create([range])}}}function removeRangeAround(sel,pos){for(let i=0;i<sel.ranges.length;i++){let{from,to}=sel.ranges[i];if(from<=pos&&to>=pos)return state.EditorSelection.create(sel.ranges.slice(0,i).concat(sel.ranges.slice(i+1)),sel.mainIndex==i?0:sel.mainIndex-(sel.mainIndex>i?1:0))}return null}function dropText(view,event,text,direct){if(text=textFilter(view.state,clipboardInputFilter,text),!text)return;let dropPos=view.posAtCoords({x:event.clientX,y:event.clientY},!1),{draggedContent}=view.inputState,del=direct&&draggedContent&&dragMovesSelection(view,event)?{from:draggedContent.from,to:draggedContent.to}:null,ins={from:dropPos,insert:text},changes=view.state.changes(del?[del,ins]:ins);view.focus(),view.dispatch({changes,selection:{anchor:changes.mapPos(dropPos,-1),head:changes.mapPos(dropPos,1)},userEvent:del?"move.drop":"input.drop"}),view.inputState.draggedContent=null}function captureCopy(view,text){// The extra wrapper is somehow necessary on IE/Edge to prevent the
|
|
136
|
+
// content from being mangled when it is put onto the clipboard
|
|
137
|
+
let parent=view.dom.parentNode;if(!parent)return;let target=parent.appendChild(document.createElement("textarea"));target.style.cssText="position: fixed; left: -10000px; top: 10px",target.value=text,target.focus(),target.selectionEnd=text.length,target.selectionStart=0,setTimeout(()=>{target.remove(),view.focus()},50)}function copiedRange(state){let content=[],ranges=[],linewise=!1;for(let range of state.selection.ranges)range.empty||(content.push(state.sliceDoc(range.from,range.to)),ranges.push(range));if(!content.length){// Nothing selected, do a line-wise copy
|
|
138
|
+
let upto=-1;for(let{from}of state.selection.ranges){let line=state.doc.lineAt(from);line.number>upto&&(content.push(line.text),ranges.push({from:line.from,to:Math.min(state.doc.length,line.to+1)})),upto=line.number}linewise=!0}return{text:textFilter(state,clipboardOutputFilter,content.join(state.lineBreak)),ranges,linewise}}function focusChangeTransaction(state,focus){let effects=[];for(let getEffect of state.facet(focusChangeEffect)){let effect=getEffect(state,focus);effect&&effects.push(effect)}return effects.length?state.update({effects,annotations:isFocusChange.of(!0)}):null}function updateForFocusChange(view){setTimeout(()=>{let focus=view.hasFocus;if(focus!=view.inputState.notifiedFocused){let tr=focusChangeTransaction(view.state,focus);tr?view.dispatch(tr):view.update([])}},10)}// In Firefox, when cut/copy handlers are added to the document, that
|
|
139
|
+
// somehow avoids a bug where those events aren't fired when the
|
|
140
|
+
// selection is empty. See issue #1082 and
|
|
141
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=995961
|
|
142
|
+
function firefoxCopyCutHack(doc){appliedFirefoxHack.has(doc)||(appliedFirefoxHack.add(doc),doc.addEventListener("copy",()=>{}),doc.addEventListener("cut",()=>{}))}function clearHeightChangeFlag(){heightChangeFlag=!1}function replace(old,val){return old==val?old:(old.constructor!=val.constructor&&(heightChangeFlag=!0),val)}function mergeGaps(nodes,around){let before,after;null==nodes[around]&&(before=nodes[around-1])instanceof HeightMapGap&&(after=nodes[around+1])instanceof HeightMapGap&&nodes.splice(around-1,3,new HeightMapGap(before.length+1+after.length))}function heightRelevantDecoChanges(a,b,diff){let comp=new DecorationComparator;return state.RangeSet.compare(a,b,diff,comp,0),comp.changes}function visiblePixelRange(dom,paddingTop){let rect=dom.getBoundingClientRect(),doc=dom.ownerDocument,win=doc.defaultView||window,left=Math.max(0,rect.left),right=Math.min(win.innerWidth,rect.right),top=Math.max(0,rect.top),bottom=Math.min(win.innerHeight,rect.bottom);for(let parent=dom.parentNode;parent&&parent!=doc.body;)if(1==parent.nodeType){let elt=parent,style=window.getComputedStyle(elt);if((elt.scrollHeight>elt.clientHeight||elt.scrollWidth>elt.clientWidth)&&"visible"!=style.overflow){let parentRect=elt.getBoundingClientRect();left=Math.max(left,parentRect.left),right=Math.min(right,parentRect.right),top=Math.max(top,parentRect.top),bottom=Math.min(parent==dom.parentNode?win.innerHeight:bottom,parentRect.bottom)}parent="absolute"==style.position||"fixed"==style.position?elt.offsetParent:elt.parentNode}else if(11==parent.nodeType)// Shadow root
|
|
143
|
+
parent=parent.host;else break;return{left:left-rect.left,right:Math.max(left,right)-rect.left,top:top-(rect.top+paddingTop),bottom:Math.max(top,bottom)-(rect.top+paddingTop)}}function inWindow(elt){let rect=elt.getBoundingClientRect(),win=elt.ownerDocument.defaultView||window;return rect.left<win.innerWidth&&0<rect.right&&rect.top<win.innerHeight&&0<rect.bottom}function fullPixelRange(dom,paddingTop){let rect=dom.getBoundingClientRect();return{left:0,right:rect.right-rect.left,top:paddingTop,bottom:rect.bottom-(rect.top+paddingTop)}}// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
144
|
+
// lines within the viewport, as a kludge to keep the editor
|
|
145
|
+
// responsive when a ridiculously long line is loaded into it.
|
|
146
|
+
function lineStructure(from,to,stateDeco){let ranges=[],pos=from,total=0;return state.RangeSet.spans(stateDeco,from,to,{span(){},point(from,to){from>pos&&(ranges.push({from:pos,to:from}),total+=from-pos),pos=to}},20),pos<to&&(ranges.push({from:pos,to}),total+=to-pos),{total,ranges}}function findPosition({total,ranges},ratio){if(0>=ratio)return ranges[0].from;if(1<=ratio)return ranges[ranges.length-1].to;let dist=Math.floor(total*ratio);for(let i=0;;i++){let{from,to}=ranges[i],size=to-from;if(dist<=size)return from+dist;dist-=size}}function findFraction(structure,pos){let counted=0;for(let{from,to}of structure.ranges){if(pos<=to){counted+=pos-from;break}counted+=to-from}return counted/structure.total}function find(array,f){for(let val of array)if(f(val))return val}// Don't scale when the document height is within the range of what
|
|
147
|
+
// the DOM can handle.
|
|
148
|
+
function staticDeco(state$1){let deco=state$1.facet(decorations).filter(d=>"function"!=typeof d),outer=state$1.facet(outerDecorations).filter(d=>"function"!=typeof d);return outer.length&&deco.push(state.RangeSet.join(outer)),deco}// When the height is too big (> VP.MaxDOMHeight), scale down the
|
|
149
|
+
// regions outside the viewports so that the total height is
|
|
150
|
+
// VP.MaxDOMHeight.
|
|
151
|
+
function scaleBlock(block,scaler){if(1==scaler.scale)return block;let bTop=scaler.toDOM(block.top),bBottom=scaler.toDOM(block.bottom);return new BlockInfo(block.from,block.length,bTop,bBottom-bTop,Array.isArray(block._content)?block._content.map(b=>scaleBlock(b,scaler)):block._content)}function buildTheme(main,spec,scopes){return new styleMod.StyleModule(spec,{finish(sel){return /&/.test(sel)?sel.replace(/&\w*/,m=>{if("&"==m)return main;if(!scopes||!scopes[m])throw new RangeError(`Unsupported selector: ${m}`);return scopes[m]}):main+" "+sel}})}function findChild(tile,dom,dir){for(;dom;){let curTile=Tile.get(dom);if(curTile&&curTile.parent==tile)return curTile;let parent=dom.parentNode;dom=parent==tile.dom?0<dir?dom.nextSibling:dom.previousSibling:parent}return null}function buildSelectionRangeFromRange(view,range){let anchorNode=range.startContainer,anchorOffset=range.startOffset,focusNode=range.endContainer,focusOffset=range.endOffset,curAnchor=view.docView.domAtPos(view.state.selection.main.anchor,1);// Since such a range doesn't distinguish between anchor and head,
|
|
152
|
+
// use a heuristic that flips it around if its end matches the
|
|
153
|
+
// current anchor.
|
|
154
|
+
return isEquivalentPosition(curAnchor.node,curAnchor.offset,focusNode,focusOffset)&&([anchorNode,anchorOffset,focusNode,focusOffset]=[focusNode,focusOffset,anchorNode,anchorOffset]),{anchorNode,anchorOffset,focusNode,focusOffset}}// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
155
|
+
function safariSelectionRangeHack(view,selection){// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
156
|
+
// access to the selection inside a shadowroot, we have to perform a
|
|
157
|
+
// ridiculous hack to get at it—using `execCommand` to trigger a
|
|
158
|
+
// `beforeInput` event so that we can read the target range from the
|
|
159
|
+
// event.
|
|
160
|
+
function read(event){event.preventDefault(),event.stopImmediatePropagation(),found=event.getTargetRanges()[0]}if(selection.getComposedRanges){let range=selection.getComposedRanges(view.root)[0];if(range)return buildSelectionRangeFromRange(view,range)}let found=null;return view.contentDOM.addEventListener("beforeinput",read,!0),view.dom.ownerDocument.execCommand("indent"),view.contentDOM.removeEventListener("beforeinput",read,!0),found?buildSelectionRangeFromRange(view,found):null}function attrsFromFacet(view,facet,base){for(let sources=view.state.facet(facet),i=sources.length-1;0<=i;i--){let source=sources[i],value="function"==typeof source?source(view):source;value&&combineAttrs(value,base)}return base}function normalizeKeyName(name,platform){const parts=name.split(/-(?!$)/);let result=parts[parts.length-1];"Space"==result&&(result=" ");let alt,ctrl,shift,meta;for(let i=0;i<parts.length-1;++i){const mod=parts[i];if(/^(cmd|meta|m)$/i.test(mod))meta=!0;else if(/^a(lt)?$/i.test(mod))alt=!0;else if(/^(c|ctrl|control)$/i.test(mod))ctrl=!0;else if(/^s(hift)?$/i.test(mod))shift=!0;else if(/^mod$/i.test(mod))"mac"==platform?meta=!0:ctrl=!0;else throw new Error("Unrecognized modifier name: "+mod)}return alt&&(result="Alt-"+result),ctrl&&(result="Ctrl-"+result),meta&&(result="Meta-"+result),shift&&(result="Shift-"+result),result}function modifiers(name,event,shift){return event.altKey&&(name="Alt-"+name),event.ctrlKey&&(name="Ctrl-"+name),event.metaKey&&(name="Meta-"+name),!1!==shift&&event.shiftKey&&(name="Shift-"+name),name}// This is hidden behind an indirection, rather than directly computed
|
|
161
|
+
// by the facet, to keep internal types out of the facet's type.
|
|
162
|
+
function getKeymap(state){let bindings=state.facet(keymap),map=Keymaps.get(bindings);return map||Keymaps.set(bindings,map=buildKeymap(bindings.reduce((a,b)=>a.concat(b),[]))),map}/**
|
|
163
|
+
Run the key handlers registered for a given scope. The event
|
|
164
|
+
object should be a `"keydown"` event. Returns true if any of the
|
|
165
|
+
handlers handled it.
|
|
166
|
+
*/function runScopeHandlers(view,event,scope){return runHandlers(getKeymap(view.state),event,view,scope)}function buildKeymap(bindings,platform=currentPlatform){let bound=Object.create(null),isPrefix=Object.create(null),checkPrefix=(name,is)=>{let current=isPrefix[name];if(null==current)isPrefix[name]=is;else if(current!=is)throw new Error("Key binding "+name+" is used both as a regular binding and as a multi-stroke prefix")},add=(scope,key,command,preventDefault,stopPropagation)=>{var _a,_b;let scopeObj=bound[scope]||(bound[scope]=Object.create(null)),parts=key.split(/ (?!$)/).map(k=>normalizeKeyName(k,platform));for(let i=1,prefix;i<parts.length;i++)prefix=parts.slice(0,i).join(" "),checkPrefix(prefix,!0),scopeObj[prefix]||(scopeObj[prefix]={preventDefault:!0,stopPropagation:!1,run:[view=>{let ourObj=storedPrefix={view,prefix,scope};return setTimeout(()=>{storedPrefix==ourObj&&(storedPrefix=null)},PrefixTimeout),!0}]});let full=parts.join(" ");checkPrefix(full,!1);let binding=scopeObj[full]||(scopeObj[full]={preventDefault:!1,stopPropagation:!1,run:(null===(_b=null===(_a=scopeObj._any)||void 0===_a?void 0:_a.run)||void 0===_b?void 0:_b.slice())||[]});command&&binding.run.push(command),preventDefault&&(binding.preventDefault=!0),stopPropagation&&(binding.stopPropagation=!0)};for(let b of bindings){let scopes=b.scope?b.scope.split(" "):["editor"];if(b.any)for(let scope of scopes){let scopeObj=bound[scope]||(bound[scope]=Object.create(null));scopeObj._any||(scopeObj._any={preventDefault:!1,stopPropagation:!1,run:[]});let{any}=b;for(let key in scopeObj)scopeObj[key].run.push(view=>any(view,currentKeyEvent))}let name=b[platform]||b.key;if(name)for(let scope of scopes)add(scope,name,b.run,b.preventDefault,b.stopPropagation),b.shift&&add(scope,"Shift-"+name,b.shift,b.preventDefault,b.stopPropagation)}return bound}function runHandlers(map,event,view,scope){currentKeyEvent=event;let name=w3cKeyname.keyName(event),charCode=state.codePointAt(name,0),isChar=state.codePointSize(charCode)==name.length&&" "!=name,prefix="",handled=!1,prevented=!1,stopPropagation=!1;storedPrefix&&storedPrefix.view==view&&storedPrefix.scope==scope&&(prefix=storedPrefix.prefix+" ",0>modifierCodes.indexOf(event.keyCode)&&(prevented=!0,storedPrefix=null));let ran=new Set,runFor=binding=>{if(binding){for(let cmd of binding.run)if(!ran.has(cmd)&&(ran.add(cmd),cmd(view)))return binding.stopPropagation&&(stopPropagation=!0),!0;binding.preventDefault&&(binding.stopPropagation&&(stopPropagation=!0),prevented=!0)}return!1},scopeObj=map[scope],baseName,shiftName;return scopeObj&&(runFor(scopeObj[prefix+modifiers(name,event,!isChar)])?handled=!0:isChar&&(event.altKey||event.metaKey||event.ctrlKey)&&// Ctrl-Alt may be used for AltGr on Windows
|
|
167
|
+
!(browser.windows&&event.ctrlKey&&event.altKey)&&// Alt-combinations on macOS tend to be typed characters
|
|
168
|
+
!(browser.mac&&event.altKey&&!(event.ctrlKey||event.metaKey))&&(baseName=w3cKeyname.base[event.keyCode])&&baseName!=name?runFor(scopeObj[prefix+modifiers(baseName,event,!0)])?handled=!0:event.shiftKey&&(shiftName=w3cKeyname.shift[event.keyCode])!=name&&shiftName!=baseName&&runFor(scopeObj[prefix+modifiers(shiftName,event,!1)])&&(handled=!0):isChar&&event.shiftKey&&runFor(scopeObj[prefix+modifiers(name,event,!0)])&&(handled=!0),!handled&&runFor(scopeObj._any)&&(handled=!0)),prevented&&(handled=!0),handled&&stopPropagation&&event.stopPropagation(),currentKeyEvent=null,handled}/**
|
|
169
|
+
Implementation of [`LayerMarker`](https://codemirror.net/6/docs/ref/#view.LayerMarker) that creates
|
|
170
|
+
a rectangle at a given set of coordinates.
|
|
171
|
+
*/function getBase(view){let rect=view.scrollDOM.getBoundingClientRect(),left=view.textDirection==exports.Direction.LTR?rect.left:rect.right-view.scrollDOM.clientWidth*view.scaleX;return{left:left-view.scrollDOM.scrollLeft*view.scaleX,top:rect.top-view.scrollDOM.scrollTop*view.scaleY}}function wrappedLine(view,pos,side,inside){let coords=view.coordsAtPos(pos,2*side);if(!coords)return inside;let editorRect=view.dom.getBoundingClientRect(),y=(coords.top+coords.bottom)/2,left=view.posAtCoords({x:editorRect.left+1,y}),right=view.posAtCoords({x:editorRect.right-1,y});return null==left||null==right?inside:{from:Math.max(inside.from,Math.min(left,right)),to:Math.min(inside.to,Math.max(left,right))}}function rectanglesForRange(view,className,range){function piece(left,top,right,bottom){return new RectangleMarker(className,left-base.left,top-base.top,Math.max(0,right-left),bottom-top)}function pieces({top,bottom,horizontal}){let pieces=[];for(let i=0;i<horizontal.length;i+=2)pieces.push(piece(horizontal[i],top,horizontal[i+1],bottom));return pieces}// Gets passed from/to in line-local positions
|
|
172
|
+
function drawForLine(from,to,line){function addSpan(from,fromOpen,to,toOpen,dir){// Passing 2/-2 is a kludge to force the view to return
|
|
173
|
+
// coordinates on the proper side of block widgets, since
|
|
174
|
+
// normalizing the side there, though appropriate for most
|
|
175
|
+
// coordsAtPos queries, would break selection drawing.
|
|
176
|
+
let fromCoords=view.coordsAtPos(from,from==line.to?-2:2),toCoords=view.coordsAtPos(to,to==line.from?2:-2);fromCoords&&toCoords&&(top=Math.min(fromCoords.top,toCoords.top,top),bottom=Math.max(fromCoords.bottom,toCoords.bottom,bottom),dir==exports.Direction.LTR?horizontal.push(ltr&&fromOpen?leftSide:fromCoords.left,ltr&&toOpen?rightSide:toCoords.right):horizontal.push(!ltr&&toOpen?leftSide:toCoords.left,!ltr&&fromOpen?rightSide:fromCoords.right))}let top=1e9,bottom=-1e9,horizontal=[],start=null!==from&&void 0!==from?from:line.from,end=null!==to&&void 0!==to?to:line.to;// Split the range by visible range and document line
|
|
177
|
+
for(let r of view.visibleRanges)if(r.to>start&&r.from<end)for(let pos=Math.max(r.from,start),endPos=Math.min(r.to,end),docLine;;){docLine=view.state.doc.lineAt(pos);for(let span of view.bidiSpans(docLine)){let spanFrom=span.from+docLine.from,spanTo=span.to+docLine.from;if(spanFrom>=endPos)break;spanTo>pos&&addSpan(Math.max(spanFrom,pos),null==from&&spanFrom<=start,Math.min(spanTo,endPos),null==to&&spanTo>=end,span.dir)}if(pos=docLine.to+1,pos>=endPos)break}return 0==horizontal.length&&addSpan(start,null==from,end,null==to,view.textDirection),{top,bottom,horizontal}}function drawForWidget(block,top){let y=contentRect.top+(top?block.top:block.bottom);return{top:y,bottom:y,horizontal:[]}}if(range.to<=view.viewport.from||range.from>=view.viewport.to)return[];let from=Math.max(range.from,view.viewport.from),to=Math.min(range.to,view.viewport.to),ltr=view.textDirection==exports.Direction.LTR,content=view.contentDOM,contentRect=content.getBoundingClientRect(),base=getBase(view),lineElt=content.querySelector(".cm-line"),lineStyle=lineElt&&window.getComputedStyle(lineElt),leftSide=contentRect.left+(lineStyle?parseInt(lineStyle.paddingLeft)+Math.min(0,parseInt(lineStyle.textIndent)):0),rightSide=contentRect.right-(lineStyle?parseInt(lineStyle.paddingRight):0),startBlock=blockAt(view,from,1),endBlock=blockAt(view,to,-1),visualStart=startBlock.type==exports.BlockType.Text?startBlock:null,visualEnd=endBlock.type==exports.BlockType.Text?endBlock:null;if(visualStart&&(view.lineWrapping||startBlock.widgetLineBreaks)&&(visualStart=wrappedLine(view,from,1,visualStart)),visualEnd&&(view.lineWrapping||endBlock.widgetLineBreaks)&&(visualEnd=wrappedLine(view,to,-1,visualEnd)),visualStart&&visualEnd&&visualStart.from==visualEnd.from&&visualStart.to==visualEnd.to)return pieces(drawForLine(range.from,range.to,visualStart));else{let top=visualStart?drawForLine(range.from,null,visualStart):drawForWidget(startBlock,!1),bottom=visualEnd?drawForLine(null,range.to,visualEnd):drawForWidget(endBlock,!0),between=[];return(visualStart||startBlock).to<(visualEnd||endBlock).from-(visualStart&&visualEnd?1:0)||1<startBlock.widgetLineBreaks&&top.bottom+view.defaultLineHeight/2<bottom.top?between.push(piece(leftSide,top.bottom,rightSide,bottom.top)):top.bottom<bottom.top&&view.elementAtHeight((top.bottom+bottom.top)/2).type==exports.BlockType.Text&&(top.bottom=bottom.top=(top.bottom+bottom.top)/2),pieces(top).concat(between).concat(pieces(bottom))}}function sameMarker(a,b){return a.constructor==b.constructor&&a.eq(b)}/**
|
|
178
|
+
Define a layer.
|
|
179
|
+
*/function layer(config){return[ViewPlugin.define(v=>new LayerView(v,config)),layerOrder.of(config)]}/**
|
|
180
|
+
Returns an extension that hides the browser's native selection and
|
|
181
|
+
cursor, replacing the selection with a background behind the text
|
|
182
|
+
(with the `cm-selectionBackground` class), and the
|
|
183
|
+
cursors with elements overlaid over the code (using
|
|
184
|
+
`cm-cursor-primary` and `cm-cursor-secondary`).
|
|
185
|
+
|
|
186
|
+
This allows the editor to display secondary selection ranges, and
|
|
187
|
+
tends to produce a type of selection more in line with that users
|
|
188
|
+
expect in a text editor (the native selection styling will often
|
|
189
|
+
leave gaps between lines and won't fill the horizontal space after
|
|
190
|
+
a line when the selection continues past it).
|
|
191
|
+
|
|
192
|
+
It does have a performance cost, in that it requires an extra DOM
|
|
193
|
+
layout cycle for many updates (the selection is drawn based on DOM
|
|
194
|
+
layout information that's only available after laying out the
|
|
195
|
+
content).
|
|
196
|
+
*/function drawSelection(config={}){return[selectionConfig.of(config),cursorLayer,selectionLayer,hideNativeSelection,nativeSelectionHidden.of(!0)]}/**
|
|
197
|
+
Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration
|
|
198
|
+
for this state. (Note that this will return a set of defaults even
|
|
199
|
+
if `drawSelection` isn't enabled.)
|
|
200
|
+
*/function getDrawSelectionConfig(state){return state.facet(selectionConfig)}function configChanged(update){return update.startState.facet(selectionConfig)!=update.state.facet(selectionConfig)}function setBlinkRate(state,dom){dom.style.animationDuration=state.facet(selectionConfig).cursorBlinkRate+"ms"}/**
|
|
201
|
+
Draws a cursor at the current drop position when something is
|
|
202
|
+
dragged over the editor.
|
|
203
|
+
*/function dropCursor(){return[dropCursorPos,drawDropCursor]}function iterMatches(doc,re,from,to,f){re.lastIndex=0;for(let cursor=doc.iterRange(from,to),pos=from,m;!cursor.next().done;pos+=cursor.value.length)if(!cursor.lineBreak)for(;m=re.exec(cursor.value);)f(pos+m.index,m)}function matchRanges(view,maxLength){let visible=view.visibleRanges;if(1==visible.length&&visible[0].from==view.viewport.from&&visible[0].to==view.viewport.to)return visible;let result=[];for(let{from,to}of visible)from=Math.max(view.state.doc.lineAt(from).from,from-maxLength),to=Math.min(view.state.doc.lineAt(to).to,to+maxLength),result.length&&result[result.length-1].to>=from?result[result.length-1].to=to:result.push({from,to});return result}/**
|
|
204
|
+
Helper class used to make it easier to maintain decorations on
|
|
205
|
+
visible code that matches a given regular expression. To be used
|
|
206
|
+
in a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin). Instances of this object
|
|
207
|
+
represent a matching configuration.
|
|
208
|
+
*/function supportsTabSize(){var _a;if(null==_supportsTabSize&&"undefined"!=typeof document&&document.body){let styles=document.body.style;_supportsTabSize=null!=(null!==(_a=styles.tabSize)&&void 0!==_a?_a:styles.MozTabSize)}return _supportsTabSize||!1}/**
|
|
209
|
+
Returns an extension that installs highlighting of special
|
|
210
|
+
characters.
|
|
211
|
+
*/function highlightSpecialChars(/**
|
|
212
|
+
Configuration options.
|
|
213
|
+
*/config={}){return[specialCharConfig.of(config),specialCharPlugin()]}function specialCharPlugin(){return _plugin||(_plugin=ViewPlugin.fromClass(class{constructor(view){this.view=view,this.decorations=Decoration.none,this.decorationCache=Object.create(null),this.decorator=this.makeDecorator(view.state.facet(specialCharConfig)),this.decorations=this.decorator.createDeco(view)}makeDecorator(conf){return new MatchDecorator({regexp:conf.specialChars,decoration:(m,view,pos)=>{let{doc}=view.state,code=state.codePointAt(m[0],0);if(9==code){let line=doc.lineAt(pos),size=view.state.tabSize,col=state.countColumn(line.text,size,pos-line.from);return Decoration.replace({widget:new TabWidget((size-col%size)*this.view.defaultCharacterWidth/this.view.scaleX)})}return this.decorationCache[code]||(this.decorationCache[code]=Decoration.replace({widget:new SpecialCharWidget(conf,code)}))},boundary:conf.replaceTabs?void 0:/[^]/})}update(update){let conf=update.state.facet(specialCharConfig);update.startState.facet(specialCharConfig)==conf?this.decorations=this.decorator.updateDeco(update,this.decorations):(this.decorator=this.makeDecorator(conf),this.decorations=this.decorator.createDeco(update.view))}},{decorations:v=>v.decorations}))}// Assigns placeholder characters from the Control Pictures block to
|
|
214
|
+
// ASCII control characters
|
|
215
|
+
function placeholder$1(code){return 32<=code?"\u2022":10==code?"\u2424":String.fromCharCode(9216+code)}/**
|
|
216
|
+
Returns an extension that makes sure the content has a bottom
|
|
217
|
+
margin equivalent to the height of the editor, minus one line
|
|
218
|
+
height, so that every line in the document can be scrolled to the
|
|
219
|
+
top of the editor.
|
|
220
|
+
|
|
221
|
+
This is only meaningful when the editor is scrollable, and should
|
|
222
|
+
not be enabled in editors that take the size of their content.
|
|
223
|
+
*/function scrollPastEnd(){return[plugin,contentAttributes.of(view=>{var _a;return(null===(_a=view.plugin(plugin))||void 0===_a?void 0:_a.attrs)||null})]}/**
|
|
224
|
+
Mark lines that have a cursor on them with the `"cm-activeLine"`
|
|
225
|
+
DOM class.
|
|
226
|
+
*/function highlightActiveLine(){return activeLineHighlighter}/**
|
|
227
|
+
Extension that enables a placeholder—a piece of example content
|
|
228
|
+
to show when the editor is empty.
|
|
229
|
+
*/function placeholder(content){let plugin=ViewPlugin.fromClass(class{constructor(view){this.view=view,this.placeholder=content?Decoration.set([Decoration.widget({widget:new Placeholder(content),side:1}).range(0)]):Decoration.none}get decorations(){return this.view.state.doc.length?Decoration.none:this.placeholder}},{decorations:v=>v.decorations});return"string"==typeof content?[plugin,EditorView.contentAttributes.of({"aria-placeholder":content})]:plugin}// Don't compute precise column positions for line offsets above this
|
|
230
|
+
// (since it could get expensive). Assume offset==column for them.
|
|
231
|
+
function rectangleFor(state$1,a,b){let startLine=Math.min(a.line,b.line),endLine=Math.max(a.line,b.line),ranges=[];if(a.off>MaxOff||b.off>MaxOff||0>a.col||0>b.col){let startOff=Math.min(a.off,b.off),endOff=Math.max(a.off,b.off);for(let i=startLine,line;i<=endLine;i++)line=state$1.doc.line(i),line.length<=endOff&&ranges.push(state.EditorSelection.range(line.from+startOff,line.to+endOff))}else{let startCol=Math.min(a.col,b.col),endCol=Math.max(a.col,b.col);for(let i=startLine;i<=endLine;i++){let line=state$1.doc.line(i),start=state.findColumn(line.text,startCol,state$1.tabSize,!0);if(0>start)ranges.push(state.EditorSelection.cursor(line.to));else{let end=state.findColumn(line.text,endCol,state$1.tabSize);ranges.push(state.EditorSelection.range(line.from+start,line.from+end))}}}return ranges}function absoluteColumn(view,x){let ref=view.coordsAtPos(view.viewport.from);return ref?Math.round(Math.abs((ref.left-x)/view.defaultCharacterWidth)):-1}function getPos(view,event){let offset=view.posAtCoords({x:event.clientX,y:event.clientY},!1),line=view.state.doc.lineAt(offset),off=offset-line.from,col=off>MaxOff?-1:off==line.length?absoluteColumn(view,event.clientX):state.countColumn(line.text,view.state.tabSize,offset-line.from);return{line:line.number,col,off}}function rectangleSelectionStyle(view,event){let start=getPos(view,event),startSel=view.state.selection;return start?{update(update){if(update.docChanged){let newStart=update.changes.mapPos(update.startState.doc.line(start.line).from),newLine=update.state.doc.lineAt(newStart);start={line:newLine.number,col:start.col,off:Math.min(start.off,newLine.length)},startSel=startSel.map(update.changes)}},get(event,_extend,multiple){let cur=getPos(view,event);if(!cur)return startSel;let ranges=rectangleFor(view.state,start,cur);return ranges.length?multiple?state.EditorSelection.create(ranges.concat(startSel.ranges)):state.EditorSelection.create(ranges):startSel}}:null}/**
|
|
232
|
+
Create an extension that enables rectangular selections. By
|
|
233
|
+
default, it will react to left mouse drag with the Alt key held
|
|
234
|
+
down. When such a selection occurs, the text within the rectangle
|
|
235
|
+
that was dragged over will be selected, as one selection
|
|
236
|
+
[range](https://codemirror.net/6/docs/ref/#state.SelectionRange) per line.
|
|
237
|
+
*/function rectangularSelection(options){let filter=(null===options||void 0===options?void 0:options.eventFilter)||(e=>e.altKey&&0==e.button);return EditorView.mouseSelectionStyle.of((view,event)=>filter(event)?rectangleSelectionStyle(view,event):null)}/**
|
|
238
|
+
Returns an extension that turns the pointer cursor into a
|
|
239
|
+
crosshair when a given modifier key, defaulting to Alt, is held
|
|
240
|
+
down. Can serve as a visual hint that rectangular selection is
|
|
241
|
+
going to happen when paired with
|
|
242
|
+
[`rectangularSelection`](https://codemirror.net/6/docs/ref/#view.rectangularSelection).
|
|
243
|
+
*/function crosshairCursor(options={}){let[code,getter]=keys[options.key||"Alt"],plugin=ViewPlugin.fromClass(class{constructor(view){this.view=view,this.isDown=!1}set(isDown){this.isDown!=isDown&&(this.isDown=isDown,this.view.update([]))}},{eventObservers:{keydown(e){this.set(e.keyCode==code||getter(e))},keyup(e){e.keyCode!=code&&getter(e)||this.set(!1)},mousemove(e){this.set(getter(e))}}});return[plugin,EditorView.contentAttributes.of(view=>{var _a;return(null===(_a=view.plugin(plugin))||void 0===_a?void 0:_a.isDown)?showCrosshair:null})]}/**
|
|
244
|
+
Creates an extension that configures tooltip behavior.
|
|
245
|
+
*/function tooltips(config={}){return tooltipConfig.of(config)}function windowSpace(view){let docElt=view.dom.ownerDocument.documentElement;return{top:0,left:0,bottom:docElt.clientHeight,right:docElt.clientWidth}}function setLeftStyle(elt,value){let current=parseInt(elt.style.left,10);(isNaN(current)||1<Math.abs(value-current))&&(elt.style.left=value+"px")}function isInTooltip(tooltip,event){let{left,right,top,bottom}=tooltip.getBoundingClientRect(),arrow;if(arrow=tooltip.querySelector(".cm-tooltip-arrow")){let arrowRect=arrow.getBoundingClientRect();top=Math.min(arrowRect.top,top),bottom=Math.max(arrowRect.bottom,bottom)}return event.clientX>=left-tooltipMargin&&event.clientX<=right+tooltipMargin&&event.clientY>=top-tooltipMargin&&event.clientY<=bottom+tooltipMargin}function isOverRange(view,from,to,x,y,margin){let rect=view.scrollDOM.getBoundingClientRect(),docBottom=view.documentTop+view.documentPadding.top+view.contentHeight;if(rect.left>x||rect.right<x||rect.top>y||Math.min(rect.bottom,docBottom)<y)return!1;let pos=view.posAtCoords({x,y},!1);return pos>=from&&pos<=to}/**
|
|
246
|
+
Set up a hover tooltip, which shows up when the pointer hovers
|
|
247
|
+
over ranges of text. The callback is called when the mouse hovers
|
|
248
|
+
over the document text. It should, if there is a tooltip
|
|
249
|
+
associated with position `pos`, return the tooltip description
|
|
250
|
+
(either directly or in a promise). The `side` argument indicates
|
|
251
|
+
on which side of the position the pointer is—it will be -1 if the
|
|
252
|
+
pointer is before the position, 1 if after the position.
|
|
253
|
+
|
|
254
|
+
Note that all hover tooltips are hosted within a single tooltip
|
|
255
|
+
container element. This allows multiple tooltips over the same
|
|
256
|
+
range to be "merged" together without overlapping.
|
|
257
|
+
|
|
258
|
+
The return value is a valid [editor extension](https://codemirror.net/6/docs/ref/#state.Extension)
|
|
259
|
+
but also provides an `active` property holding a state field that
|
|
260
|
+
can be used to read the currently active tooltips produced by this
|
|
261
|
+
extension.
|
|
262
|
+
*/function hoverTooltip(source,options={}){let setHover=state.StateEffect.define(),locked=new WeakMap,hoverState=state.StateField.define({create(){return[]},update(value,tr){let lock=locked.get(value);if(value.length&&(options.hideOnChange&&(tr.docChanged||tr.selection)?value=[]:lock&&lock(tr)?value=[]:options.hideOn&&(value=value.filter(v=>!options.hideOn(tr,v)))),tr.docChanged&&value.length){let mapped=[];for(let tooltip of value){let newPos=tr.changes.mapPos(tooltip.pos,-1,state.MapMode.TrackDel);if(null!=newPos){let copy=Object.assign(Object.create(null),tooltip);copy.pos=newPos,null!=copy.end&&(copy.end=tr.changes.mapPos(copy.end)),mapped.push(copy)}}value=mapped}for(let effect of tr.effects)effect.is(setHover)&&(value=effect.value,lock=void 0),(effect.is(closeHoverTooltipEffect)&&!effect.value||effect.value==hoverState)&&(value=[]);return value.length&&lock&&locked.set(value,lock),value},provide:f=>showHoverTooltip.from(f)});// This would be better stored in the state field, but we've set
|
|
263
|
+
// down the type of the field in our interface, so it's indirectly
|
|
264
|
+
// stored by array identity.
|
|
265
|
+
const plugin=ViewPlugin.define(view=>new HoverPlugin(view,source,hoverState,locked,setHover,options.hoverTime||300/* Hover.Time */));return{active:hoverState,extension:[hoverState,plugin,hoverPlugin.of(plugin),showHoverTooltipHost]}}/**
|
|
266
|
+
Activate hover tooltips for the given position and side. If you
|
|
267
|
+
provide a specific hover tooltip (the value returned from
|
|
268
|
+
[`hoverTooltip`](https://codemirror.net/6/docs/ref/#view.hoverTooltip)), only that one will be
|
|
269
|
+
activated. If not given, all hover tooltips at the given position
|
|
270
|
+
are triggered.
|
|
271
|
+
|
|
272
|
+
Note that tooltips opened this way don't close automatically, and
|
|
273
|
+
you'll want to pass an `until` callback or use
|
|
274
|
+
[`closeHoverTooltip`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltip)/[`closeHoverTooltips`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltips)
|
|
275
|
+
to deactivate them.
|
|
276
|
+
*/function activateHover(view,pos,side,options={}){var _a;let plugins=view.state.facet(hoverPlugin).map(p=>view.plugin(p)).filter(p=>!!p);if(options.tooltip&&options.tooltip.active){let found=plugins.find(p=>p.field==options.tooltip.active);found&&(plugins=[found])}for(let plugin of plugins)plugin.activateHover(view,pos,side,null!==(_a=options.until)&&void 0!==_a?_a:()=>!1)}/**
|
|
277
|
+
Get the active tooltip view for a given tooltip, if available.
|
|
278
|
+
*/function getTooltip(view,tooltip){let plugin=view.plugin(tooltipPlugin);if(!plugin)return null;let found=plugin.manager.tooltips.indexOf(tooltip);return 0>found?null:plugin.manager.tooltipViews[found]}/**
|
|
279
|
+
Returns true if any hover tooltips are currently active.
|
|
280
|
+
*/function hasHoverTooltips(state){return state.facet(showHoverTooltip).some(x=>x)}/**
|
|
281
|
+
Transaction effect that closes a specific hover tooltip.
|
|
282
|
+
*/function closeHoverTooltip(tooltip){return closeHoverTooltipEffect.of(tooltip.active)}/**
|
|
283
|
+
Tell the tooltip extension to recompute the position of the active
|
|
284
|
+
tooltips. This can be useful when something happens (such as a
|
|
285
|
+
re-positioning or CSS change affecting the editor) that could
|
|
286
|
+
invalidate the existing tooltip positions.
|
|
287
|
+
*/function repositionTooltips(view){let plugin=view.plugin(tooltipPlugin);plugin&&plugin.maybeMeasure()}/**
|
|
288
|
+
Configures the panel-managing extension.
|
|
289
|
+
*/function panels(config){return config?[panelConfig.of(config)]:[]}/**
|
|
290
|
+
Get the active panel created by the given constructor, if any.
|
|
291
|
+
This can be useful when you need access to your panels' DOM
|
|
292
|
+
structure.
|
|
293
|
+
*/function getPanel(view,panel){let plugin=view.plugin(panelPlugin),index=plugin?plugin.specs.indexOf(panel):-1;return-1<index?plugin.panels[index]:null}function rm(node){let next=node.nextSibling;return node.remove(),next}/**
|
|
294
|
+
Opening a panel is done by providing a constructor function for
|
|
295
|
+
the panel through this facet. (The panel is closed again when its
|
|
296
|
+
constructor is no longer provided.) Values of `null` are ignored.
|
|
297
|
+
*/ /**
|
|
298
|
+
Show a panel above or below the editor to show the user a message
|
|
299
|
+
or prompt them for input. Returns an effect that can be dispatched
|
|
300
|
+
to close the dialog, and a promise that resolves when the dialog
|
|
301
|
+
is closed or a form inside of it is submitted.
|
|
302
|
+
|
|
303
|
+
You are encouraged, if your handling of the result of the promise
|
|
304
|
+
dispatches a transaction, to include the `close` effect in it. If
|
|
305
|
+
you don't, this function will automatically dispatch a separate
|
|
306
|
+
transaction right after.
|
|
307
|
+
*/function showDialog(view,config){let promise=new Promise(r=>resolve=r),panelCtor=view=>createDialog(view,config,resolve),resolve;view.state.field(dialogField,!1)?view.dispatch({effects:openDialogEffect.of(panelCtor)}):view.dispatch({effects:state.StateEffect.appendConfig.of(dialogField.init(()=>[panelCtor]))});let close=closeDialogEffect.of(panelCtor);return{close,result:promise.then(form=>{let queue=view.win.queueMicrotask||(f=>view.win.setTimeout(f,10));return queue(()=>{-1<view.state.field(dialogField).indexOf(panelCtor)&&view.dispatch({effects:close})}),form})}}/**
|
|
308
|
+
Find the [`Panel`](https://codemirror.net/6/docs/ref/#view.Panel) for an open dialog, using a class
|
|
309
|
+
name as identifier.
|
|
310
|
+
*/function getDialog(view,className){let dialogs=view.state.field(dialogField,!1)||[];for(let open of dialogs){let panel=getPanel(view,open);if(panel&&panel.dom.classList.contains(className))return panel}return null}function createDialog(view,config,result){function done(form){panel.contains(panel.ownerDocument.activeElement)&&view.focus(),result(form)}let content=config.content?config.content(view,()=>done(null)):null;if(!content){if(content=elt("form"),config.input){let input=elt("input",config.input);/^(text|password|number|email|tel|url)$/.test(input.type)&&input.classList.add("cm-textfield"),input.name||(input.name="input"),content.appendChild(elt("label",(config.label||"")+": ",input))}else content.appendChild(document.createTextNode(config.label||""));content.appendChild(document.createTextNode(" ")),content.appendChild(elt("button",{class:"cm-button",type:"submit"},config.submitLabel||"OK"))}let forms="FORM"==content.nodeName?[content]:content.querySelectorAll("form");for(let i=0,form;i<forms.length;i++)form=forms[i],form.addEventListener("keydown",event=>{27==event.keyCode?(event.preventDefault(),done(null)):13==event.keyCode&&(event.preventDefault(),done(form))}),form.addEventListener("submit",event=>{event.preventDefault(),done(form)});let panel=elt("div",content,elt("button",{onclick:()=>done(null),"aria-label":view.state.phrase("close"),class:"cm-dialog-close",type:"button"},["\xD7"]));return config.class&&(panel.className=config.class),panel.classList.add("cm-dialog"),{dom:panel,top:config.top,mount:()=>{if(config.focus){let focus;focus="string"==typeof config.focus?content.querySelector(config.focus):content.querySelector("input")||content.querySelector("button"),focus&&"select"in focus?focus.select():focus&&"focus"in focus&&focus.focus()}}}}/**
|
|
311
|
+
A gutter marker represents a bit of information attached to a line
|
|
312
|
+
in a specific gutter. Your own custom markers have to extend this
|
|
313
|
+
class.
|
|
314
|
+
*/ /**
|
|
315
|
+
Define an editor gutter. The order in which the gutters appear is
|
|
316
|
+
determined by their extension priority.
|
|
317
|
+
*/function gutter(config){return[gutters(),activeGutters.of({...defaults,...config})]}/**
|
|
318
|
+
The gutter-drawing plugin is automatically enabled when you add a
|
|
319
|
+
gutter, but you can use this function to explicitly configure it.
|
|
320
|
+
|
|
321
|
+
Unless `fixed` is explicitly set to `false`, the gutters are
|
|
322
|
+
fixed, meaning they don't scroll along with the content
|
|
323
|
+
horizontally (except on Internet Explorer, which doesn't support
|
|
324
|
+
CSS [`position:
|
|
325
|
+
sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)).
|
|
326
|
+
*/function gutters(config){let result=[gutterView];return config&&!1===config.fixed&&result.push(unfixGutters.of(!0)),result}function asArray(val){return Array.isArray(val)?val:[val]}function advanceCursor(cursor,collect,pos){for(;cursor.value&&cursor.from<=pos;)cursor.from==pos&&collect.push(cursor.value),cursor.next()}function sameMarkers(a,b){if(a.length!=b.length)return!1;for(let i=0;i<a.length;i++)if(!a[i].compare(b[i]))return!1;return!0}/**
|
|
327
|
+
Facet used to provide markers to the line number gutter.
|
|
328
|
+
*/function formatNumber(view,number){return view.state.facet(lineNumberConfig).formatNumber(number,view.state)}/**
|
|
329
|
+
Create a line number gutter extension.
|
|
330
|
+
*/function lineNumbers(config={}){return[lineNumberConfig.of(config),gutters(),lineNumberGutter]}function maxLineNumber(lines){let last=9;for(;last<lines;)last=10*last+9;return last}/**
|
|
331
|
+
Returns an extension that adds a `cm-activeLineGutter` class to
|
|
332
|
+
all gutter elements on the [active
|
|
333
|
+
line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
|
|
334
|
+
*/function highlightActiveLineGutter(){return activeLineGutterHighlighter}function matcher(decorator){return ViewPlugin.define(view=>({decorations:decorator.createDeco(view),update(u){this.decorations=decorator.updateDeco(u,this.decorations)}}),{decorations:v=>v.decorations})}/**
|
|
335
|
+
Returns an extension that highlights whitespace, adding a
|
|
336
|
+
`cm-highlightSpace` class to stretches of spaces, and a
|
|
337
|
+
`cm-highlightTab` class to individual tab characters. By default,
|
|
338
|
+
the former are shown as faint dots, and the latter as arrows.
|
|
339
|
+
*/function highlightWhitespace(){return whitespaceHighlighter}/**
|
|
340
|
+
Returns an extension that adds a `cm-trailingSpace` class to all
|
|
341
|
+
trailing whitespace.
|
|
342
|
+
*/function highlightTrailingWhitespace(){return trailingHighlighter}/**
|
|
343
|
+
@internal
|
|
344
|
+
*/var state=require$$0,styleMod=require$$1,w3cKeyname=require$$2,elt=require$$3;let nav="undefined"==typeof navigator?{userAgent:"",vendor:"",platform:""}:navigator,doc="undefined"==typeof document?{documentElement:{style:{}}}:document;const ie_edge=/Edge\/(\d+)/.exec(nav.userAgent),ie_upto10=/MSIE \d/.test(nav.userAgent),ie_11up=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent),ie=!!(ie_upto10||ie_11up||ie_edge),gecko=!ie&&/gecko\/(\d+)/i.test(nav.userAgent),chrome=!ie&&/Chrome\/(\d+)/.exec(nav.userAgent),webkit=("webkitFontSmoothing"in doc.documentElement.style),safari=!ie&&/Apple Computer/.test(nav.vendor),ios=safari&&(/Mobile\/\w+/.test(nav.userAgent)||2<nav.maxTouchPoints);var browser={mac:ios||/Mac/.test(nav.platform),windows:/Win/.test(nav.platform),linux:/Linux|X11/.test(nav.platform),ie,ie_version:ie_upto10?doc.documentMode||6:ie_11up?+ie_11up[1]:ie_edge?+ie_edge[1]:0,gecko,gecko_version:gecko?+(/Firefox\/(\d+)/.exec(nav.userAgent)||[0,0])[1]:0,chrome:!!chrome,chrome_version:chrome?+chrome[1]:0,ios,android:/Android\b/.test(nav.userAgent),webkit,webkit_version:webkit?+(/\bAppleWebKit\/(\d+)/.exec(nav.userAgent)||[0,0])[1]:0,safari,safari_version:safari?+(/\bVersion\/(\d+(\.\d+)?)/.exec(nav.userAgent)||[0,0])[1]:0,tabSize:null==doc.documentElement.style.tabSize?"-moz-tab-size":"tab-size"};const noAttrs=Object.create(null);class WidgetType{/**
|
|
345
|
+
Compare this instance to another instance of the same type.
|
|
346
|
+
(TypeScript can't express this, but only instances of the same
|
|
347
|
+
specific class will be passed to this method.) This is used to
|
|
348
|
+
avoid redrawing widgets when they are replaced by a new
|
|
349
|
+
decoration of the same type. The default implementation just
|
|
350
|
+
returns `false`, which will cause new instances of the widget to
|
|
351
|
+
always be redrawn.
|
|
352
|
+
*/eq(widget){return!1}/**
|
|
353
|
+
Update a DOM element created by a widget of the same type (but
|
|
354
|
+
different, non-`eq` content) to reflect this widget. May return
|
|
355
|
+
true to indicate that it could update, false to indicate it
|
|
356
|
+
couldn't (in which case the widget will be redrawn). The default
|
|
357
|
+
implementation just returns false.
|
|
358
|
+
*/updateDOM(dom,view,from){return!1}/**
|
|
359
|
+
@internal
|
|
360
|
+
*/compare(other){return this==other||this.constructor==other.constructor&&this.eq(other)}/**
|
|
361
|
+
The estimated height this widget will have, to be used when
|
|
362
|
+
estimating the height of content that hasn't been drawn. May
|
|
363
|
+
return -1 to indicate you don't know. The default implementation
|
|
364
|
+
returns -1.
|
|
365
|
+
*/get estimatedHeight(){return-1}/**
|
|
366
|
+
For inline widgets that are displayed inline (as opposed to
|
|
367
|
+
`inline-block`) and introduce line breaks (through `<br>` tags
|
|
368
|
+
or textual newlines), this must indicate the amount of line
|
|
369
|
+
breaks they introduce. Defaults to 0.
|
|
370
|
+
*/get lineBreaks(){return 0}/**
|
|
371
|
+
Can be used to configure which kinds of events inside the widget
|
|
372
|
+
should be ignored by the editor. The default is to ignore all
|
|
373
|
+
events.
|
|
374
|
+
*/ignoreEvent(event){return!0}/**
|
|
375
|
+
Override the way screen coordinates for positions at/in the
|
|
376
|
+
widget are found. `pos` will be the offset into the widget, and
|
|
377
|
+
`side` the side of the position that is being queried—less than
|
|
378
|
+
zero for before, greater than zero for after, and zero for
|
|
379
|
+
directly at that position.
|
|
380
|
+
*/coordsAt(dom,pos,side){return null}/**
|
|
381
|
+
@internal
|
|
382
|
+
*/get isHidden(){return!1}/**
|
|
383
|
+
@internal
|
|
384
|
+
*/get editable(){return!1}/**
|
|
385
|
+
This is called when the an instance of the widget is removed
|
|
386
|
+
from the editor view.
|
|
387
|
+
*/destroy(dom){}}/**
|
|
388
|
+
The different types of blocks that can occur in an editor view.
|
|
389
|
+
*/exports.BlockType=void 0,function(BlockType){BlockType[BlockType.Text=0]="Text",BlockType[BlockType.WidgetBefore=1]="WidgetBefore",BlockType[BlockType.WidgetAfter=2]="WidgetAfter",BlockType[BlockType.WidgetRange=3]="WidgetRange"}(exports.BlockType||(exports.BlockType={}));/**
|
|
390
|
+
A decoration provides information on how to draw or style a piece
|
|
391
|
+
of content. You'll usually use it wrapped in a
|
|
392
|
+
[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
|
|
393
|
+
@nonabstract
|
|
394
|
+
*/class Decoration extends state.RangeValue{constructor(/**
|
|
395
|
+
@internal
|
|
396
|
+
*/startSide,/**
|
|
397
|
+
@internal
|
|
398
|
+
*/endSide,/**
|
|
399
|
+
@internal
|
|
400
|
+
*/widget,/**
|
|
401
|
+
The config object used to create this decoration. You can
|
|
402
|
+
include additional properties in there to store metadata about
|
|
403
|
+
your decoration.
|
|
404
|
+
*/spec){super(),this.startSide=startSide,this.endSide=endSide,this.widget=widget,this.spec=spec}/**
|
|
405
|
+
@internal
|
|
406
|
+
*/get heightRelevant(){return!1}/**
|
|
407
|
+
Create a mark decoration, which influences the styling of the
|
|
408
|
+
content in its range. Nested mark decorations will cause nested
|
|
409
|
+
DOM elements to be created. Nesting order is determined by
|
|
410
|
+
precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
|
|
411
|
+
the higher-precedence decorations creating the inner DOM nodes.
|
|
412
|
+
Such elements are split on line boundaries and on the boundaries
|
|
413
|
+
of lower-precedence decorations.
|
|
414
|
+
*/static mark(spec){return new MarkDecoration(spec)}/**
|
|
415
|
+
Create a widget decoration, which displays a DOM element at the
|
|
416
|
+
given position.
|
|
417
|
+
*/static widget(spec){let side=Math.max(-1e4,Math.min(1e4,spec.side||0)),block=!!spec.block;return side+=block&&!spec.inlineOrder?0<side?3e8/* Side.BlockAfter */:-4e8/* Side.BlockBefore */:0<side?1e8/* Side.InlineAfter */:-1e8/* Side.InlineBefore */,new PointDecoration(spec,side,side,block,spec.widget||null,!1)}/**
|
|
418
|
+
Create a replace decoration which replaces the given range with
|
|
419
|
+
a widget, or simply hides it.
|
|
420
|
+
*/static replace(spec){let block=!!spec.block,startSide,endSide;if(spec.isBlockGap)startSide=-5e8/* Side.GapStart */,endSide=4e8/* Side.GapEnd */;else{let{start,end}=getInclusive(spec,block);startSide=(start?block?-3e8/* Side.BlockIncStart */:-1/* Side.InlineIncStart */:5e8/* Side.NonIncStart */)-1,endSide=(end?block?2e8/* Side.BlockIncEnd */:1/* Side.InlineIncEnd */:-6e8/* Side.NonIncEnd */)+1}return new PointDecoration(spec,startSide,endSide,block,spec.widget||null,!0)}/**
|
|
421
|
+
Create a line decoration, which can add DOM attributes to the
|
|
422
|
+
line starting at the given position.
|
|
423
|
+
*/static line(spec){return new LineDecoration(spec)}/**
|
|
424
|
+
Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
|
|
425
|
+
decorated range or ranges. If the ranges aren't already sorted,
|
|
426
|
+
pass `true` for `sort` to make the library sort them for you.
|
|
427
|
+
*/static set(of,sort=!1){return state.RangeSet.of(of,sort)}/**
|
|
428
|
+
@internal
|
|
429
|
+
*/hasHeight(){return!!this.widget&&-1<this.widget.estimatedHeight}}/**
|
|
430
|
+
The empty set of decorations.
|
|
431
|
+
*/Decoration.none=state.RangeSet.empty;class MarkDecoration extends Decoration{constructor(spec){let{start,end}=getInclusive(spec);super(start?-1/* Side.InlineIncStart */:5e8/* Side.NonIncStart */,end?1/* Side.InlineIncEnd */:-6e8/* Side.NonIncEnd */,null,spec),this.tagName=spec.tagName||"span",this.attrs=spec.class&&spec.attributes?combineAttrs(spec.attributes,{class:spec.class}):spec.class?{class:spec.class}:spec.attributes||noAttrs}eq(other){return this==other||other instanceof MarkDecoration&&this.tagName==other.tagName&&attrsEq(this.attrs,other.attrs)}range(from,to=from){if(from>=to)throw new RangeError("Mark decorations may not be empty");return super.range(from,to)}}MarkDecoration.prototype.point=!1;class LineDecoration extends Decoration{constructor(spec){super(-2e8/* Side.Line */,-2e8/* Side.Line */,null,spec)}eq(other){return other instanceof LineDecoration&&this.spec.class==other.spec.class&&attrsEq(this.spec.attributes,other.spec.attributes)}range(from,to=from){if(to!=from)throw new RangeError("Line decoration ranges must be zero-length");return super.range(from,to)}}LineDecoration.prototype.mapMode=state.MapMode.TrackBefore,LineDecoration.prototype.point=!0;class PointDecoration extends Decoration{constructor(spec,startSide,endSide,block,widget,isReplace){super(startSide,endSide,widget,spec),this.block=block,this.isReplace=isReplace,this.mapMode=block?0>=startSide?state.MapMode.TrackBefore:state.MapMode.TrackAfter:state.MapMode.TrackDel}// Only relevant when this.block == true
|
|
432
|
+
get type(){return this.startSide==this.endSide?0>=this.startSide?exports.BlockType.WidgetBefore:exports.BlockType.WidgetAfter:exports.BlockType.WidgetRange}get heightRelevant(){return this.block||!!this.widget&&(5<=this.widget.estimatedHeight||0<this.widget.lineBreaks)}eq(other){return other instanceof PointDecoration&&widgetsEq(this.widget,other.widget)&&this.block==other.block&&this.startSide==other.startSide&&this.endSide==other.endSide}range(from,to=from){if(this.isReplace&&(from>to||from==to&&0<this.startSide&&0>=this.endSide))throw new RangeError("Invalid range for replacement decoration");if(!this.isReplace&&to!=from)throw new RangeError("Widget decorations can only have zero-length ranges");return super.range(from,to)}}PointDecoration.prototype.point=!0;class BlockWrapper extends state.RangeValue{constructor(tagName,attributes){super(),this.tagName=tagName,this.attributes=attributes}eq(other){return other==this||other instanceof BlockWrapper&&this.tagName==other.tagName&&attrsEq(this.attributes,other.attributes)}/**
|
|
433
|
+
Create a block wrapper object with the given tag name and
|
|
434
|
+
attributes.
|
|
435
|
+
*/static create(spec){return new BlockWrapper(spec.tagName,spec.attributes||noAttrs)}/**
|
|
436
|
+
Create a range set from the given block wrapper ranges.
|
|
437
|
+
*/static set(of,sort=!1){return state.RangeSet.of(of,sort)}}BlockWrapper.prototype.startSide=BlockWrapper.prototype.endSide=-1;class DOMSelectionState{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(domSel){return this.anchorNode==domSel.anchorNode&&this.anchorOffset==domSel.anchorOffset&&this.focusNode==domSel.focusNode&&this.focusOffset==domSel.focusOffset}setRange(range){let{anchorNode,focusNode}=range;// Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152)
|
|
438
|
+
this.set(anchorNode,Math.min(range.anchorOffset,anchorNode?maxOffset(anchorNode):0),focusNode,Math.min(range.focusOffset,focusNode?maxOffset(focusNode):0))}set(anchorNode,anchorOffset,focusNode,focusOffset){this.anchorNode=anchorNode,this.anchorOffset=anchorOffset,this.focusNode=focusNode,this.focusOffset=focusOffset}}let preventScrollSupported=null;// Safari 26 breaks preventScroll support
|
|
439
|
+
browser.safari&&26<=browser.safari_version&&(preventScrollSupported=!1);let scratchRange;class DOMPos{constructor(node,offset,precise=!0){this.node=node,this.offset=offset,this.precise=precise}static before(dom,precise){return new DOMPos(dom.parentNode,domIndex(dom),precise)}static after(dom,precise){return new DOMPos(dom.parentNode,domIndex(dom)+1,precise)}}/**
|
|
440
|
+
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
441
|
+
*/exports.Direction=void 0,function(Direction){Direction[Direction.LTR=0]="LTR",Direction[Direction.RTL=1]="RTL"}(exports.Direction||(exports.Direction={}));const LTR=exports.Direction.LTR,RTL=exports.Direction.RTL,LowTypes=dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008"),ArabicTypes=dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333"),Brackets=Object.create(null),BracketStack=[];// Character types for codepoints 0x600 to 0x6f9
|
|
442
|
+
// There's a lot more in
|
|
443
|
+
// https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
|
|
444
|
+
// which are left out to keep code size down.
|
|
445
|
+
for(let p of["()","[]","{}"]){let l=p.charCodeAt(0),r=p.charCodeAt(1);Brackets[l]=r,Brackets[r]=-l}const BidiRE=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/;/**
|
|
446
|
+
Represents a contiguous range of text that has a single direction
|
|
447
|
+
(as in left-to-right or right-to-left).
|
|
448
|
+
*/class BidiSpan{/**
|
|
449
|
+
The direction of this span.
|
|
450
|
+
*/get dir(){return this.level%2?RTL:LTR}/**
|
|
451
|
+
@internal
|
|
452
|
+
*/constructor(/**
|
|
453
|
+
The start of the span (relative to the start of the line).
|
|
454
|
+
*/from,/**
|
|
455
|
+
The end of the span.
|
|
456
|
+
*/to,/**
|
|
457
|
+
The ["bidi
|
|
458
|
+
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
|
|
459
|
+
of the span (in this context, 0 means
|
|
460
|
+
left-to-right, 1 means right-to-left, 2 means left-to-right
|
|
461
|
+
number inside right-to-left text).
|
|
462
|
+
*/level){this.from=from,this.to=to,this.level=level}/**
|
|
463
|
+
@internal
|
|
464
|
+
*/side(end,dir){return this.dir==dir==end?this.to:this.from}/**
|
|
465
|
+
@internal
|
|
466
|
+
*/forward(forward,dir){return forward==(this.dir==dir)}/**
|
|
467
|
+
@internal
|
|
468
|
+
*/static find(order,index,level,assoc){let maybe=-1;for(let i=0,span;i<order.length;i++)if(span=order[i],span.from<=index&&span.to>=index){if(span.level==level)return i;// When multiple spans match, if assoc != 0, take the one that
|
|
469
|
+
// covers that side, otherwise take the one with the minimum
|
|
470
|
+
// level.
|
|
471
|
+
(0>maybe||(0==assoc?order[maybe].level>span.level:0>assoc?span.from<index:span.to>index))&&(maybe=i)}if(0>maybe)throw new RangeError("Index out of range");return maybe}}const types=[];let movedOver="";const clickAddsSelectionRange=state.Facet.define(),dragMovesSelection$1=state.Facet.define(),mouseSelectionStyle=state.Facet.define(),exceptionSink=state.Facet.define(),updateListener=state.Facet.define(),inputHandler=state.Facet.define(),focusChangeEffect=state.Facet.define(),clipboardInputFilter=state.Facet.define(),clipboardOutputFilter=state.Facet.define(),perLineTextDirection=state.Facet.define({combine:values=>values.some(x=>x)}),nativeSelectionHidden=state.Facet.define({combine:values=>values.some(x=>x)}),scrollHandler=state.Facet.define();class ScrollTarget{constructor(range,y,x,yMargin,xMargin,// This data structure is abused to also store precise scroll
|
|
472
|
+
// snapshots, instead of a `scrollIntoView` request. When this
|
|
473
|
+
// flag is `true`, `range` points at a position in the reference
|
|
474
|
+
// line, `yMargin` holds the difference between the top of that
|
|
475
|
+
// line and the top of the editor, and `xMargin` holds the
|
|
476
|
+
// editor's `scrollLeft`.
|
|
477
|
+
isSnapshot=!1){this.range=range,this.y=y,this.x=x,this.yMargin=yMargin,this.xMargin=xMargin,this.isSnapshot=isSnapshot}map(changes){return changes.empty?this:new ScrollTarget(this.range.map(changes),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}clip(state$1){return this.range.to<=state$1.doc.length?this:new ScrollTarget(state.EditorSelection.cursor(state$1.doc.length),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}}const scrollIntoView=state.StateEffect.define({map:(t,ch)=>t.map(ch)}),setEditContextFormatting=state.StateEffect.define(),editable=state.Facet.define({combine:values=>!values.length||values[0]});let nextPluginID=0;const viewPlugin=state.Facet.define({combine(plugins){return plugins.filter((p,i)=>{for(let j=0;j<i;j++)if(plugins[j].plugin==p.plugin)return!1;return!0})}});/**
|
|
478
|
+
View plugins associate stateful values with a view. They can
|
|
479
|
+
influence the way the content is drawn, and are notified of things
|
|
480
|
+
that happen in the view. They optionally take an argument, in
|
|
481
|
+
which case you need to call [`of`](https://codemirror.net/6/docs/ref/#view.ViewPlugin.of) to create
|
|
482
|
+
an extension for the plugin. When the argument type is undefined,
|
|
483
|
+
you can use the plugin instance as an extension directly.
|
|
484
|
+
*/class ViewPlugin{constructor(/**
|
|
485
|
+
@internal
|
|
486
|
+
*/id,/**
|
|
487
|
+
@internal
|
|
488
|
+
*/create,/**
|
|
489
|
+
@internal
|
|
490
|
+
*/domEventHandlers,/**
|
|
491
|
+
@internal
|
|
492
|
+
*/domEventObservers,buildExtensions){this.id=id,this.create=create,this.domEventHandlers=domEventHandlers,this.domEventObservers=domEventObservers,this.baseExtensions=buildExtensions(this),this.extension=this.baseExtensions.concat(viewPlugin.of({plugin:this,arg:void 0}))}/**
|
|
493
|
+
Create an extension for this plugin with the given argument.
|
|
494
|
+
*/of(arg){return this.baseExtensions.concat(viewPlugin.of({plugin:this,arg}))}/**
|
|
495
|
+
Define a plugin from a constructor function that creates the
|
|
496
|
+
plugin's value, given an editor view.
|
|
497
|
+
*/static define(create,spec){const{eventHandlers,eventObservers,provide,decorations:deco}=spec||{};return new ViewPlugin(nextPluginID++,create,eventHandlers,eventObservers,plugin=>{let ext=[];return deco&&ext.push(decorations.of(view=>{let pluginInst=view.plugin(plugin);return pluginInst?deco(pluginInst):Decoration.none})),provide&&ext.push(provide(plugin)),ext})}/**
|
|
498
|
+
Create a plugin for a class whose constructor takes a single
|
|
499
|
+
editor view as argument.
|
|
500
|
+
*/static fromClass(cls,spec){return ViewPlugin.define((view,arg)=>new cls(view,arg),spec)}}class PluginInstance{constructor(spec){this.spec=spec,this.mustUpdate=null,this.value=null}get plugin(){return this.spec&&this.spec.plugin}update(view){if(!this.value){if(this.spec)try{this.value=this.spec.plugin.create(view,this.spec.arg)}catch(e){logException(view.state,e,"CodeMirror plugin crashed"),this.deactivate()}}else if(this.mustUpdate){let update=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(update)}catch(e){if(logException(update.state,e,"CodeMirror plugin crashed"),this.value.destroy)try{this.value.destroy()}catch(_){}this.deactivate()}}return this}destroy(view){var _a;if(null===(_a=this.value)||void 0===_a?void 0:_a.destroy)try{this.value.destroy()}catch(e){logException(view.state,e,"CodeMirror plugin crashed")}}deactivate(){this.spec=this.value=null}}const editorAttributes=state.Facet.define(),contentAttributes=state.Facet.define(),decorations=state.Facet.define(),blockWrappers=state.Facet.define(),outerDecorations=state.Facet.define(),atomicRanges=state.Facet.define(),bidiIsolatedRanges=state.Facet.define(),scrollMargins=state.Facet.define(),styleModule=state.Facet.define();// Provide decorations
|
|
501
|
+
class ChangedRange{constructor(fromA,toA,fromB,toB){this.fromA=fromA,this.toA=toA,this.fromB=fromB,this.toB=toB}join(other){return new ChangedRange(Math.min(this.fromA,other.fromA),Math.max(this.toA,other.toA),Math.min(this.fromB,other.fromB),Math.max(this.toB,other.toB))}addToSet(set){let i=set.length,me=this;for(;0<i;i--){let range=set[i-1];if(!(range.fromA>me.toA)){if(range.toA<me.fromA)break;me=me.join(range),set.splice(i-1,1)}}return set.splice(i,0,me),set}// Extend a set to cover all the content in `ranges`, which is a
|
|
502
|
+
// flat array with each pair of numbers representing fromB/toB
|
|
503
|
+
// positions. These pairs are generated in unchanged ranges, so the
|
|
504
|
+
// offset between doc A and doc B is the same for their start and
|
|
505
|
+
// end points.
|
|
506
|
+
static extendWithRanges(diff,ranges){if(0==ranges.length)return diff;let result=[];for(let dI=0,rI=0,off=0;;){let nextD=dI<diff.length?diff[dI].fromB:1e9,nextR=rI<ranges.length?ranges[rI]:1e9,fromB=Math.min(nextD,nextR);if(1e9==fromB)break;let fromA=fromB+off,toB=fromB,toA=fromA;for(;;)if(rI<ranges.length&&ranges[rI]<=toB){let end=ranges[rI+1];rI+=2,toB=Math.max(toB,end);for(let i=dI;i<diff.length&&diff[i].fromB<=toB;i++)off=diff[i].toA-diff[i].toB;toA=Math.max(toA,end+off)}else if(dI<diff.length&&diff[dI].fromB<=toB){let next=diff[dI++];toB=Math.max(toB,next.toB),toA=Math.max(toA,next.toA),off=next.toA-next.toB}else break;result.push(new ChangedRange(fromA,toA,fromB,toB))}return result}}/**
|
|
507
|
+
View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this
|
|
508
|
+
class, which describe what happened, whenever the view is updated.
|
|
509
|
+
*/class ViewUpdate{constructor(/**
|
|
510
|
+
The editor view that the update is associated with.
|
|
511
|
+
*/view,/**
|
|
512
|
+
The new editor state.
|
|
513
|
+
*/state$1,/**
|
|
514
|
+
The transactions involved in the update. May be empty.
|
|
515
|
+
*/transactions){this.view=view,this.state=state$1,this.transactions=transactions,this.flags=0,this.startState=view.state,this.changes=state.ChangeSet.empty(this.startState.doc.length);for(let tr of transactions)this.changes=this.changes.compose(tr.changes);let changedRanges=[];this.changes.iterChangedRanges((fromA,toA,fromB,toB)=>changedRanges.push(new ChangedRange(fromA,toA,fromB,toB))),this.changedRanges=changedRanges}/**
|
|
516
|
+
@internal
|
|
517
|
+
*/static create(view,state,transactions){return new ViewUpdate(view,state,transactions)}/**
|
|
518
|
+
Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
|
|
519
|
+
[visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this
|
|
520
|
+
update.
|
|
521
|
+
*/get viewportChanged(){return 0<(4&this.flags/* UpdateFlag.Viewport */)}/**
|
|
522
|
+
Returns true when
|
|
523
|
+
[`viewportChanged`](https://codemirror.net/6/docs/ref/#view.ViewUpdate.viewportChanged) is true
|
|
524
|
+
and the viewport change is not just the result of mapping it in
|
|
525
|
+
response to document changes.
|
|
526
|
+
*/get viewportMoved(){return 0<(8&this.flags/* UpdateFlag.ViewportMoved */)}/**
|
|
527
|
+
Indicates whether the height of a block element in the editor
|
|
528
|
+
changed in this update.
|
|
529
|
+
*/get heightChanged(){return 0<(2&this.flags/* UpdateFlag.Height */)}/**
|
|
530
|
+
Returns true when the document was modified or the size of the
|
|
531
|
+
editor, or elements within the editor, changed.
|
|
532
|
+
*/get geometryChanged(){return this.docChanged||0<(18/* UpdateFlag.Height */&this.flags)}/**
|
|
533
|
+
True when this update indicates a focus change.
|
|
534
|
+
*/get focusChanged(){return 0<(1&this.flags/* UpdateFlag.Focus */)}/**
|
|
535
|
+
Whether the document changed in this update.
|
|
536
|
+
*/get docChanged(){return!this.changes.empty}/**
|
|
537
|
+
Whether the selection was explicitly set in this update.
|
|
538
|
+
*/get selectionSet(){return this.transactions.some(tr=>tr.selection)}/**
|
|
539
|
+
@internal
|
|
540
|
+
*/get empty(){return 0==this.flags&&0==this.transactions.length}}const noChildren=[];class Tile{constructor(dom,length,flags=0){this.dom=dom,this.length=length,this.flags=flags,this.parent=null,dom.cmTile=this}get breakAfter(){return 1&this.flags/* TileFlag.BreakAfter */}get children(){return noChildren}isWidget(){return!1}get isHidden(){return!1}isComposite(){return!1}isLine(){return!1}isText(){return!1}isBlock(){return!1}get domAttrs(){return null}sync(track){if(this.flags|=2/* TileFlag.Synced */,4&this.flags/* TileFlag.AttrsDirty */){this.flags&=-5/* TileFlag.AttrsDirty */;let attrs=this.domAttrs;attrs&&setAttrs(this.dom,attrs)}}toString(){return this.constructor.name+(this.children.length?`(${this.children})`:"")+(this.breakAfter?"#":"")}destroy(){this.parent=null}setDOM(dom){this.dom=dom,dom.cmTile=this}get posAtStart(){return this.parent?this.parent.posBefore(this):0}get posAtEnd(){return this.posAtStart+this.length}posBefore(tile,start=this.posAtStart){let pos=start;for(let child of this.children){if(child==tile)return pos;pos+=child.length+child.breakAfter}throw new RangeError("Invalid child in posBefore")}posAfter(tile){return this.posBefore(tile)+tile.length}covers(side){return!0}coordsIn(pos,side){return null}domPosFor(off,side){let index=domIndex(this.dom),after=this.length?0<off:0<side;return new DOMPos(this.parent.dom,index+(after?1:0),0==off||off==this.length)}markDirty(attrs){this.flags&=-3/* TileFlag.Synced */,attrs&&(this.flags|=4/* TileFlag.AttrsDirty */),this.parent&&2&this.parent.flags/* TileFlag.Synced */&&this.parent.markDirty(!1)}get overrideDOMText(){return null}get root(){for(let t=this;t;t=t.parent)if(t instanceof DocTile)return t;return null}static get(dom){return dom.cmTile}}class CompositeTile extends Tile{constructor(dom){super(dom,0),this._children=[]}isComposite(){return!0}get children(){return this._children}get lastChild(){return this.children.length?this.children[this.children.length-1]:null}append(child){this.children.push(child),child.parent=this}sync(track){if(2&this.flags/* TileFlag.Synced */)return;super.sync(track);let parent=this.dom,prev=null,tracking=(null===track||void 0===track?void 0:track.node)==parent?track:null,length=0,next;for(let child of this.children){if(child.sync(track),length+=child.length+child.breakAfter,next=prev?prev.nextSibling:parent.firstChild,tracking&&next!=child.dom&&(tracking.written=!0),child.dom.parentNode==parent)for(;next&&next!=child.dom;)next=rm$1(next);else parent.insertBefore(child.dom,next);prev=child.dom}for(next=prev?prev.nextSibling:parent.firstChild,tracking&&next&&(tracking.written=!0);next;)next=rm$1(next);this.length=length}}class DocTile extends CompositeTile{constructor(view,dom){super(dom),this.view=view}owns(tile){for(;tile;tile=tile.parent)if(tile==this)return!0;return!1}isBlock(){return!0}nearest(dom){for(;;){if(!dom)return null;let tile=Tile.get(dom);if(tile&&this.owns(tile))return tile;dom=dom.parentNode}}blockTiles(f){for(let stack=[],cur=this,i=0,pos=0;;)if(i==cur.children.length){if(!stack.length)return;cur=cur.parent,cur.breakAfter&&pos++,i=stack.pop()}else{let next=cur.children[i++];if(next instanceof BlockWrapperTile)stack.push(i),cur=next,i=0;else{let end=pos+next.length,result=f(next,pos);if(void 0!==result)return result;pos=end+next.breakAfter}}}// Find the block at the given position. If side < -1, make sure to
|
|
541
|
+
// stay before block widgets at that position, if side > 1, after
|
|
542
|
+
// such widgets (used for selection drawing, which needs to be able
|
|
543
|
+
// to get coordinates for positions that aren't valid cursor positions).
|
|
544
|
+
resolveBlock(pos,side){let beforeOff=-1,afterOff=-1,before,after;if(this.blockTiles((tile,off)=>{let end=off+tile.length;if(pos>=off&&pos<=end){if(tile.isWidget()&&-1<=side&&1>=side){if(32&tile.flags/* TileFlag.After */)return!0;16&tile.flags/* TileFlag.Before */&&(before=void 0)}(off<pos||pos==end&&(-1>side?tile.length:tile.covers(1)))&&(!before||!tile.isWidget()&&before.isWidget())&&(before=tile,beforeOff=pos-off),(end>pos||pos==off&&(1<side?tile.length:tile.covers(-1)))&&(!after||!tile.isWidget()&&after.isWidget())&&(after=tile,afterOff=pos-off)}}),!before&&!after)throw new Error("No tile at position "+pos);return before&&0>side||!after?{tile:before,offset:beforeOff}:{tile:after,offset:afterOff}}}class BlockWrapperTile extends CompositeTile{constructor(dom,wrapper){super(dom),this.wrapper=wrapper}isBlock(){return!0}covers(side){return!!this.children.length&&(0>side?this.children[0].covers(-1):this.lastChild.covers(1))}get domAttrs(){return this.wrapper.attributes}static of(wrapper,dom){let tile=new BlockWrapperTile(dom||document.createElement(wrapper.tagName),wrapper);return dom||(tile.flags|=4/* TileFlag.AttrsDirty */),tile}}class LineTile extends CompositeTile{constructor(dom,attrs){super(dom),this.attrs=attrs}isLine(){return!0}static start(attrs,dom,keepAttrs){let line=new LineTile(dom||document.createElement("div"),attrs);return dom&&keepAttrs||(line.flags|=4/* TileFlag.AttrsDirty */),line}get domAttrs(){return this.attrs}// Find the tile associated with a given position in this line.
|
|
545
|
+
resolveInline(pos,side,forCoords){function scan(tile,pos){for(let i=0,off=0;i<tile.children.length&&off<=pos;i++){let child=tile.children[i],end=off+child.length;end>=pos&&(child.isComposite()?scan(child,pos-off):(!after||after.isHidden&&(0<side||forCoords&&onSameLine(after,child)))&&(end>pos||32&child.flags/* TileFlag.After */)?(after=child,afterOff=pos-off):(off<pos||16&child.flags/* TileFlag.Before */&&!child.isHidden)&&(before=child,beforeOff=pos-off)),off=end}}let before=null,beforeOff=-1,after=null,afterOff=-1;scan(this,pos);let target=(0>side?before:after)||before||after;return target?{tile:target,offset:target==before?beforeOff:afterOff}:null}coordsIn(pos,side){let found=this.resolveInline(pos,side,!0);return found?found.tile.coordsIn(Math.max(0,found.offset),side):fallbackRect(this)}domIn(pos,side){let found=this.resolveInline(pos,side);if(found){let{tile,offset}=found;if(this.dom.contains(tile.dom))return tile.isText()?new DOMPos(tile.dom,Math.min(tile.dom.nodeValue.length,offset)):tile.domPosFor(offset,16&tile.flags/* TileFlag.Before */?1:32&tile.flags/* TileFlag.After */?-1:side);let parent=found.tile.parent,saw=!1;for(let ch of parent.children){if(saw)return new DOMPos(ch.dom,0);ch==found.tile&&(saw=!0)}}return new DOMPos(this.dom,0)}}class MarkTile extends CompositeTile{constructor(dom,mark){super(dom),this.mark=mark}get domAttrs(){return this.mark.attrs}static of(mark,dom){let tile=new MarkTile(dom||document.createElement(mark.tagName),mark);return dom||(tile.flags|=4/* TileFlag.AttrsDirty */),tile}}class TextTile extends Tile{constructor(dom,text){super(dom,text.length),this.text=text}sync(track){2&this.flags/* TileFlag.Synced */||(super.sync(track),this.dom.nodeValue!=this.text&&(track&&track.node==this.dom&&(track.written=!0),this.dom.nodeValue=this.text))}isText(){return!0}toString(){return JSON.stringify(this.text)}coordsIn(pos,side){let length=this.dom.nodeValue.length;pos>length&&(pos=length);let from=pos,to=pos,flatten=0;0==pos&&0>side||pos==length&&0<=side?!(browser.chrome||browser.gecko)&&(pos?(from--,flatten=1):to<length&&(to++,flatten=-1)):0>side?from--:to<length&&to++;let rects=textRange(this.dom,from,to).getClientRects();if(!rects.length)return null;let rect=rects[(flatten?0>flatten:0<=side)?0:rects.length-1];return browser.safari&&!flatten&&0==rect.width&&(rect=Array.prototype.find.call(rects,r=>r.width)||rect),flatten?flattenRect(rect,0>flatten):rect||null}static of(text,dom){let tile=new TextTile(dom||document.createTextNode(text),text);return dom||(tile.flags|=2/* TileFlag.Synced */),tile}}class WidgetTile extends Tile{constructor(dom,length,widget,flags){super(dom,length,flags),this.widget=widget}isWidget(){return!0}get isHidden(){return this.widget.isHidden}covers(side){return!(48&this.flags/* TileFlag.PointWidget */)&&0<(this.flags&(0>side?64/* TileFlag.IncStart */:128/* TileFlag.IncEnd */))}coordsIn(pos,side){return this.coordsInWidget(pos,side,!1)}coordsInWidget(pos,side,block){let custom=this.widget.coordsAt(this.dom,pos,side);if(custom)return custom;if(block)return flattenRect(this.dom.getBoundingClientRect(),this.length?0==pos:0>=side);else{let rects=this.dom.getClientRects(),rect=null;if(!rects.length)return null;let fromBack=!!(16&this.flags/* TileFlag.Before */)||!(32&this.flags/* TileFlag.After */)&&0<pos;for(let i=fromBack?rects.length-1:0;;i+=fromBack?-1:1)if(rect=rects[i],0<pos?0==i:i==rects.length-1||rect.top<rect.bottom)break;return flattenRect(rect,!fromBack)}}get overrideDOMText(){if(!this.length)return state.Text.empty;let{root}=this;if(!root)return state.Text.empty;let start=this.posAtStart;return root.view.state.doc.slice(start,start+this.length)}destroy(){super.destroy(),this.widget.destroy(this.dom)}static of(widget,view,length,flags,dom){return dom||(dom=widget.toDOM(view),!widget.editable&&(dom.contentEditable="false")),new WidgetTile(dom,length,widget,flags)}}// These are drawn around uneditable widgets to avoid a number of
|
|
546
|
+
// browser bugs that show up when the cursor is directly next to
|
|
547
|
+
// uneditable inline content.
|
|
548
|
+
class WidgetBufferTile extends Tile{constructor(flags){let img=document.createElement("img");img.className="cm-widgetBuffer",img.setAttribute("aria-hidden","true"),super(img,0,flags)}get isHidden(){return!0}get overrideDOMText(){return state.Text.empty}coordsIn(pos){return this.dom.getBoundingClientRect()}}// Represents a position in the tile tree.
|
|
549
|
+
class TilePointer{constructor(top){this.index=0,this.beforeBreak=!1,this.parents=[],this.tile=top}// Advance by the given distance. If side is -1, stop leaving or
|
|
550
|
+
// entering tiles, or skipping zero-length tiles, once the distance
|
|
551
|
+
// has been traversed. When side is 1, leave, enter, or skip
|
|
552
|
+
// everything at the end position.
|
|
553
|
+
advance(dist,side,walker){let{tile,index,beforeBreak,parents}=this;for(;dist||0<side;)if(!tile.isComposite()){if(index==tile.length)beforeBreak=!!tile.breakAfter,({tile,index}=parents.pop()),index++;else if(!dist)break;else{let take=Math.min(dist,tile.length-index);walker&&walker.skip(tile,index,index+take),dist-=take,index+=take}}else if(beforeBreak){if(!dist)break;walker&&walker.break(),dist--,beforeBreak=!1}else if(index==tile.children.length){if(!dist&&!parents.length)break;walker&&walker.leave(tile),beforeBreak=!!tile.breakAfter,({tile,index}=parents.pop()),index++}else{let next=tile.children[index],brk=next.breakAfter;(0<side?!(next.length<=dist):!(next.length<dist))||walker&&!1===walker.skip(next,0,next.length)&&next.isComposite?(parents.push({tile,index}),tile=next,index=0,walker&&next.isComposite()&&walker.enter(next)):(beforeBreak=!!brk,index++,dist-=next.length)}return this.tile=tile,this.index=index,this.beforeBreak=beforeBreak,this}get root(){return this.parents.length?this.parents[0].tile:this.tile}}// Used to track open block wrappers
|
|
554
|
+
class OpenWrapper{constructor(from,to,wrapper,rank){this.from=from,this.to=to,this.wrapper=wrapper,this.rank=rank}}// This class builds up a new document tile using input from either
|
|
555
|
+
// iteration over the old tree or iteration over the document +
|
|
556
|
+
// decorations. The add* methods emit elements into the tile
|
|
557
|
+
// structure. To avoid awkward synchronization issues, marks and block
|
|
558
|
+
// wrappers are treated as belonging to to their content, rather than
|
|
559
|
+
// opened/closed independently.
|
|
560
|
+
//
|
|
561
|
+
// All composite tiles that are touched by changes are rebuilt,
|
|
562
|
+
// reusing as much of the old tree (either whole nodes or just DOM
|
|
563
|
+
// elements) as possible. The new tree is built without the Synced
|
|
564
|
+
// flag, and then synced (during which DOM parent/child relations are
|
|
565
|
+
// fixed up, text nodes filled in, and attributes added) in a second
|
|
566
|
+
// phase.
|
|
567
|
+
class TileBuilder{constructor(cache,root,blockWrappers){this.cache=cache,this.root=root,this.blockWrappers=blockWrappers,this.curLine=null,this.lastBlock=null,this.afterWidget=null,this.pos=0,this.wrappers=[],this.wrapperPos=0}addText(text,marks,openStart,tile){var _a;this.flushBuffer();let parent=this.ensureMarks(marks,openStart),prev=parent.lastChild;if(prev&&prev.isText()&&!(8&prev.flags/* TileFlag.Composition */)&&512>prev.length+text.length/* C.Chunk */){this.cache.reused.set(prev,2/* Reused.DOM */);let tile=parent.children[parent.children.length-1]=new TextTile(prev.dom,prev.text+text);tile.parent=parent}else parent.append(tile||TextTile.of(text,null===(_a=this.cache.find(TextTile))||void 0===_a?void 0:_a.dom));this.pos+=text.length,this.afterWidget=null}addComposition(composition,context){let line=this.curLine;line.dom!=context.line.dom&&(line.setDOM(this.cache.reused.has(context.line)?freeNode(context.line.dom):context.line.dom),this.cache.reused.set(context.line,2/* Reused.DOM */));let head=line;for(let i=context.marks.length-1;0<=i;i--){let mark=context.marks[i],last=head.lastChild;if(last instanceof MarkTile&&last.mark.eq(mark.mark))last.dom!=mark.dom&&last.setDOM(freeNode(mark.dom)),head=last;else{if(this.cache.reused.get(mark)){let tile=Tile.get(mark.dom);tile&&tile.setDOM(freeNode(mark.dom))}let nw=MarkTile.of(mark.mark,mark.dom);head.append(nw),head=nw}this.cache.reused.set(mark,2/* Reused.DOM */)}let oldTile=Tile.get(composition.text);oldTile&&this.cache.reused.set(oldTile,2/* Reused.DOM */);let text=new TextTile(composition.text,composition.text.nodeValue);text.flags|=8/* TileFlag.Composition */,this.pos=composition.range.toB,head.append(text)}addInlineWidget(widget,marks,openStart){// Adjacent same-side-facing non-replacing widgets don't need buffers between them
|
|
568
|
+
let noSpace=this.afterWidget&&48&widget.flags/* TileFlag.PointWidget */&&(48&this.afterWidget.flags/* TileFlag.PointWidget */)==(48&widget.flags/* TileFlag.PointWidget */);noSpace||this.flushBuffer();let parent=this.ensureMarks(marks,openStart);noSpace||16&widget.flags/* TileFlag.Before */||parent.append(this.getBuffer(1)),parent.append(widget),this.pos+=widget.length,this.afterWidget=widget}addMark(tile,marks,openStart){this.flushBuffer();let parent=this.ensureMarks(marks,openStart);parent.append(tile),this.pos+=tile.length,this.afterWidget=null}addBlockWidget(widget){this.getBlockPos().append(widget),this.pos+=widget.length,this.lastBlock=widget,this.endLine()}continueWidget(length){let widget=this.afterWidget||this.lastBlock;widget.length+=length,this.pos+=length}addLineStart(attrs,dom){var _a;attrs||(attrs=lineBaseAttrs);let tile=LineTile.start(attrs,dom||(null===(_a=this.cache.find(LineTile))||void 0===_a?void 0:_a.dom),!!dom);this.getBlockPos().append(this.lastBlock=this.curLine=tile)}addLine(tile){this.getBlockPos().append(tile),this.pos+=tile.length,this.lastBlock=tile,this.endLine()}addBreak(){this.lastBlock.flags|=1/* TileFlag.BreakAfter */,this.endLine(),this.pos++}addLineStartIfNotCovered(attrs){this.blockPosCovered()||this.addLineStart(attrs)}ensureLine(attrs){this.curLine||this.addLineStart(attrs)}ensureMarks(marks,openStart){var _a;let parent=this.curLine;for(let i=marks.length-1;0<=i;i--){let mark=marks[i],last;if(0<openStart&&(last=parent.lastChild)&&last instanceof MarkTile&&last.mark.eq(mark))parent=last,openStart--;else{let tile=MarkTile.of(mark,null===(_a=this.cache.find(MarkTile,m=>m.mark.eq(mark)))||void 0===_a?void 0:_a.dom);parent.append(tile),parent=tile,openStart=0}}return parent}endLine(){if(this.curLine){this.flushBuffer();let last=this.curLine.lastChild;last&&hasContent(this.curLine,!1)&&("BR"==last.dom.nodeName||!last.isWidget()||browser.ios&&hasContent(this.curLine,!0))||this.curLine.append(this.cache.findWidget(BreakWidget,0,32/* TileFlag.After */)||new WidgetTile(BreakWidget.toDOM(),0,BreakWidget,32/* TileFlag.After */)),this.curLine=this.afterWidget=null}}updateBlockWrappers(){this.wrapperPos>this.pos+1e4/* C.WrapperReset */&&(this.blockWrappers.goto(this.pos),this.wrappers.length=0);for(let i=this.wrappers.length-1;0<=i;i--)this.wrappers[i].to<this.pos&&this.wrappers.splice(i,1);for(let cur=this.blockWrappers;cur.value&&cur.from<=this.pos;cur.next())if(cur.to>=this.pos){let wrap=new OpenWrapper(cur.from,cur.to,cur.value,cur.rank),i=this.wrappers.length;for(;0<i&&0>(this.wrappers[i-1].rank-wrap.rank||this.wrappers[i-1].to-wrap.to);)i--;this.wrappers.splice(i,0,wrap)}this.wrapperPos=this.pos}getBlockPos(){var _a;this.updateBlockWrappers();let parent=this.root;for(let wrap of this.wrappers){let last=parent.lastChild;if(wrap.from<this.pos&&last instanceof BlockWrapperTile&&last.wrapper.eq(wrap.wrapper))parent=last;else{let tile=BlockWrapperTile.of(wrap.wrapper,null===(_a=this.cache.find(BlockWrapperTile,t=>t.wrapper.eq(wrap.wrapper)))||void 0===_a?void 0:_a.dom);parent.append(tile),parent=tile}}return parent}blockPosCovered(){let last=this.lastBlock;return null!=last&&!last.breakAfter&&(!last.isWidget()||0<(160/* TileFlag.IncEnd */&last.flags))}getBuffer(side){let flags=2/* TileFlag.Synced */|(0>side?16/* TileFlag.Before */:32/* TileFlag.After */),found=this.cache.find(WidgetBufferTile,void 0,1/* Reused.Full */);return found&&(found.flags=flags),found||new WidgetBufferTile(flags)}flushBuffer(){this.afterWidget&&!(32&this.afterWidget.flags/* TileFlag.After */)&&(this.afterWidget.parent.append(this.getBuffer(-1)),this.afterWidget=null)}}// Helps getting efficient access to the document text.
|
|
569
|
+
class TextStream{constructor(doc){this.skipCount=0,this.text="",this.textOff=0,this.cursor=doc.iter()}skip(len){this.textOff+len<=this.text.length?this.textOff+=len:(this.skipCount+=len-(this.text.length-this.textOff),this.text="",this.textOff=0)}next(maxLen){if(this.textOff==this.text.length){let{value,lineBreak,done}=this.cursor.next(this.skipCount);if(this.skipCount=0,done)throw new Error("Ran out of text content when drawing inline views");this.text=value;let len=this.textOff=Math.min(maxLen,value.length);return lineBreak?null:value.slice(0,len)}let end=Math.min(this.text.length,this.textOff+maxLen),chars=this.text.slice(this.textOff,end);return this.textOff=end,chars}}// Assign the tile classes bucket numbers for caching.
|
|
570
|
+
const buckets=[WidgetTile,LineTile,TextTile,MarkTile,WidgetBufferTile,BlockWrapperTile,DocTile];for(let i=0;i<buckets.length;i++)buckets[i].bucket=i;// Leaf tiles and line tiles may be reused in their entirety. All
|
|
571
|
+
// others will get new tiles allocated, using the old DOM when
|
|
572
|
+
// possible.
|
|
573
|
+
class TileCache{constructor(view){this.view=view,this.buckets=buckets.map(()=>[]),this.index=buckets.map(()=>0),this.reused=new Map}// Put a tile in the cache.
|
|
574
|
+
add(tile){let i=tile.constructor.bucket,bucket=this.buckets[i];6>bucket.length/* C.Bucket */?bucket.push(tile):bucket[this.index[i]=(this.index[i]+1)%6/* C.Bucket */]=tile}find(cls,test,type=2/* Reused.DOM */){let i=cls.bucket,bucket=this.buckets[i],off=this.index[i];for(let j=bucket.length-1;0<=j;j--){// Look at the most recently added items first (last-in, first-out)
|
|
575
|
+
let index=(j+off)%bucket.length,tile=bucket[index];if((!test||test(tile))&&!this.reused.has(tile))return bucket.splice(index,1),index<off&&this.index[i]--,this.reused.set(tile,type),tile}return null}findWidget(widget,length,flags){let widgets=this.buckets[0];if(widgets.length)for(let i=0,pass=0;;i++){if(i==widgets.length){if(pass)return null;pass=1,i=0}let tile=widgets[i];if(!this.reused.has(tile)&&(0==pass?tile.widget.compare(widget):tile.widget.constructor==widget.constructor&&widget.updateDOM(tile.dom,this.view,tile.widget)))return widgets.splice(i,1),i<this.index[0]&&this.index[0]--,tile.widget==widget&&tile.length==length&&(497/* TileFlag.BreakAfter */&tile.flags)==flags?(this.reused.set(tile,1/* Reused.Full */),tile):(this.reused.set(tile,2/* Reused.DOM */),new WidgetTile(tile.dom,length,widget,-498&tile.flags|flags))}}reuse(tile){return this.reused.set(tile,1/* Reused.Full */),tile}maybeReuse(tile,type=2/* Reused.DOM */){if(!this.reused.has(tile))return this.reused.set(tile,type),tile.dom}clear(){for(let i=0;i<this.buckets.length;i++)this.buckets[i].length=this.index[i]=0}}// This class organizes a pass over the document, guided by the array
|
|
576
|
+
// of replaced ranges. For ranges that haven't changed, it iterates
|
|
577
|
+
// the old tree and copies its content into the new document. For
|
|
578
|
+
// changed ranges, it runs a decoration iterator to guide generation
|
|
579
|
+
// of content.
|
|
580
|
+
class TileUpdate{constructor(view,old,blockWrappers,decorations,disallowBlockEffectsFor){this.view=view,this.decorations=decorations,this.disallowBlockEffectsFor=disallowBlockEffectsFor,this.openWidget=!1,this.openMarks=0,this.cache=new TileCache(view),this.text=new TextStream(view.state.doc),this.builder=new TileBuilder(this.cache,new DocTile(view,view.contentDOM),state.RangeSet.iter(blockWrappers)),this.cache.reused.set(old,2/* Reused.DOM */),this.old=new TilePointer(old),this.reuseWalker={skip:(tile,from,to)=>{if(this.cache.add(tile),tile.isComposite())return!1},enter:tile=>this.cache.add(tile),leave:()=>{},break:()=>{}}}run(changes,composition){let compositionContext=composition&&this.getCompositionContext(composition.text);for(let posA=0,posB=0,i=0;;){let next=i<changes.length?changes[i++]:null,skipA=next?next.fromA:this.old.root.length;if(skipA>posA){let len=skipA-posA;this.preserve(len,!i,!next),posA=skipA,posB+=len}if(!next)break;// Compositions need to be handled specially, forcing the
|
|
581
|
+
// focused text node and its parent nodes to remain stable at
|
|
582
|
+
// that point in the document.
|
|
583
|
+
composition&&next.fromA<=composition.range.fromA&&next.toA>=composition.range.toA?(this.forward(next.fromA,composition.range.fromA,composition.range.fromA<composition.range.toA?1:-1),this.emit(posB,composition.range.fromB),this.cache.clear(),this.builder.addComposition(composition,compositionContext),this.text.skip(composition.range.toB-composition.range.fromB),this.forward(composition.range.fromA,next.toA),this.emit(composition.range.toB,next.toB)):(this.forward(next.fromA,next.toA),this.emit(posB,next.toB)),posB=next.toB,posA=next.toA}return this.builder.curLine&&this.builder.endLine(),this.builder.root}preserve(length,incStart,incEnd){let activeMarks=getMarks(this.old),openMarks=this.openMarks;this.old.advance(length,incEnd?1:-1,{skip:(tile,from,to)=>{if(tile.isWidget()){if(this.openWidget)this.builder.continueWidget(to-from);else{let widget=0<to||from<tile.length?WidgetTile.of(tile.widget,this.view,to-from,496&tile.flags/* TileFlag.Widget */,this.cache.maybeReuse(tile)):this.cache.reuse(tile);256&widget.flags/* TileFlag.Block */?(widget.flags&=-2/* TileFlag.BreakAfter */,this.builder.addBlockWidget(widget)):(this.builder.ensureLine(null),this.builder.addInlineWidget(widget,activeMarks,openMarks),openMarks=activeMarks.length)}}else if(tile.isText())this.builder.ensureLine(null),from||to!=tile.length||this.cache.reused.has(tile)?(this.cache.add(tile),this.builder.addText(tile.text.slice(from,to),activeMarks,openMarks)):this.builder.addText(tile.text,activeMarks,openMarks,this.cache.reuse(tile)),openMarks=activeMarks.length;else if(tile.isLine())tile.flags&=-2/* TileFlag.BreakAfter */,this.cache.reused.set(tile,1/* Reused.Full */),this.builder.addLine(tile);else if(tile instanceof WidgetBufferTile)this.cache.add(tile);else if(tile instanceof MarkTile)this.builder.ensureLine(null),this.builder.addMark(tile,activeMarks,openMarks),this.cache.reused.set(tile,1/* Reused.Full */),openMarks=activeMarks.length;else return!1;this.openWidget=!1},enter:tile=>{tile.isLine()?this.builder.addLineStart(tile.attrs,this.cache.maybeReuse(tile)):(this.cache.add(tile),tile instanceof MarkTile&&activeMarks.unshift(tile.mark)),this.openWidget=!1},leave:tile=>{tile.isLine()?activeMarks.length&&(activeMarks.length=openMarks=0):tile instanceof MarkTile&&(activeMarks.shift(),openMarks=Math.min(openMarks,activeMarks.length))},break:()=>{this.builder.addBreak(),this.openWidget=!1}}),this.text.skip(length)}emit(from,to){let pendingLineAttrs=null,b=this.builder,markCount=0,openEnd=state.RangeSet.spans(this.decorations,from,to,{point:(from,to,deco,active,openStart,index)=>{if(deco instanceof PointDecoration){if(this.disallowBlockEffectsFor[index]){if(deco.block)throw new RangeError("Block decorations may not be specified via plugins");if(to>this.view.state.doc.lineAt(from).to)throw new RangeError("Decorations that replace line breaks may not be specified via plugins")}if(markCount=active.length,openStart>active.length)b.continueWidget(to-from);else{let widget=deco.widget||(deco.block?NullWidget.block:NullWidget.inline),flags=widgetFlags(deco),tile=this.cache.findWidget(widget,to-from,flags)||WidgetTile.of(widget,this.view,to-from,flags);deco.block?(0<deco.startSide&&b.addLineStartIfNotCovered(pendingLineAttrs),b.addBlockWidget(tile)):(b.ensureLine(pendingLineAttrs),b.addInlineWidget(tile,active,openStart))}pendingLineAttrs=null}else pendingLineAttrs=addLineDeco(pendingLineAttrs,deco);to>from&&this.text.skip(to-from)},span:(from,to,active,openStart)=>{for(let pos=from,chars;pos<to;)chars=this.text.next(Math.min(512/* C.Chunk */,to-pos)),null==chars?(b.addLineStartIfNotCovered(pendingLineAttrs),b.addBreak(),pos++):(b.ensureLine(pendingLineAttrs),b.addText(chars,active,pos==from?openStart:active.length),pos+=chars.length),pendingLineAttrs=null}});b.addLineStartIfNotCovered(pendingLineAttrs),this.openWidget=openEnd>markCount,this.openMarks=openEnd}forward(from,to,side=1){10>=to-from?this.old.advance(to-from,side,this.reuseWalker):(this.old.advance(5,-1,this.reuseWalker),this.old.advance(to-from-10,-1),this.old.advance(5,side,this.reuseWalker))}getCompositionContext(text){let marks=[],line=null;for(let parent=text.parentNode,tile;;parent=parent.parentNode){if(tile=Tile.get(parent),parent==this.view.contentDOM)break;if(tile instanceof MarkTile)marks.push(tile);else if(null===tile||void 0===tile?void 0:tile.isLine())line=tile;else if(tile instanceof BlockWrapperTile);// Ignore
|
|
584
|
+
else"DIV"!=parent.nodeName||line||parent==this.view.contentDOM?line||marks.push(MarkTile.of(new MarkDecoration({tagName:parent.nodeName.toLowerCase(),attributes:getAttrs(parent)}),parent)):line=new LineTile(parent,lineBaseAttrs)}return{line:line,marks}}}const lineBaseAttrs={class:"cm-line"};class NullWidget extends WidgetType{constructor(tag){super(),this.tag=tag}eq(other){return other.tag==this.tag}toDOM(){return document.createElement(this.tag)}updateDOM(elt){return elt.nodeName.toLowerCase()==this.tag}get isHidden(){return!0}}NullWidget.inline=new NullWidget("span"),NullWidget.block=new NullWidget("div");const BreakWidget=new class extends WidgetType{toDOM(){return document.createElement("br")}get isHidden(){return!0}get editable(){return!0}};class DocView{constructor(view){this.view=view,this.decorations=[],this.blockWrappers=[],this.dynamicDecorationMap=[!1],this.domChanged=null,this.hasComposition=null,this.editContextFormatting=Decoration.none,this.lastCompositionAfterCursor=!1,this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.updateDeco(),this.tile=new DocTile(view,view.contentDOM),this.updateInner([new ChangedRange(0,0,0,view.state.doc.length)],null)}// Update the document view to a given state.
|
|
585
|
+
update(update){var _a;let changedRanges=update.changedRanges;0<this.minWidth&&changedRanges.length&&(changedRanges.every(({fromA,toA})=>toA<this.minWidthFrom||fromA>this.minWidthTo)?(this.minWidthFrom=update.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=update.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0),this.updateEditContextFormatting(update);let readCompositionAt=-1;0<=this.view.inputState.composing&&!this.view.observer.editContext&&((null===(_a=this.domChanged)||void 0===_a?void 0:_a.newSel)?readCompositionAt=this.domChanged.newSel.head:!touchesComposition(update.changes,this.hasComposition)&&!update.selectionSet&&(readCompositionAt=update.state.selection.main.head));let composition=-1<readCompositionAt?findCompositionRange(this.view,update.changes,readCompositionAt):null;if(this.domChanged=null,this.hasComposition){let{from,to}=this.hasComposition;changedRanges=new ChangedRange(from,to,update.changes.mapPos(from,-1),update.changes.mapPos(to,1)).addToSet(changedRanges.slice())}this.hasComposition=composition?{from:composition.range.fromB,to:composition.range.toB}:null,(browser.ie||browser.chrome)&&!composition&&update&&update.state.doc.lines!=update.startState.doc.lines&&(this.forceSelection=!0);let prevDeco=this.decorations,prevWrappers=this.blockWrappers;this.updateDeco();let decoDiff=findChangedDeco(prevDeco,this.decorations,update.changes);decoDiff.length&&(changedRanges=ChangedRange.extendWithRanges(changedRanges,decoDiff));let blockDiff=findChangedWrappers(prevWrappers,this.blockWrappers,update.changes);return blockDiff.length&&(changedRanges=ChangedRange.extendWithRanges(changedRanges,blockDiff)),composition&&!changedRanges.some(r=>r.fromA<=composition.range.fromA&&r.toA>=composition.range.toA)&&(changedRanges=composition.range.addToSet(changedRanges.slice())),!(2&this.tile.flags/* TileFlag.Synced */&&0==changedRanges.length)&&(this.updateInner(changedRanges,composition),update.transactions.length&&(this.lastUpdate=Date.now()),!0)}// Used by update and the constructor do perform the actual DOM
|
|
586
|
+
// update
|
|
587
|
+
updateInner(changes,composition){this.view.viewState.mustMeasureContent=!0;let{observer}=this.view;observer.ignore(()=>{if(composition||changes.length){let oldTile=this.tile,builder=new TileUpdate(this.view,oldTile,this.blockWrappers,this.decorations,this.dynamicDecorationMap);composition&&Tile.get(composition.text)&&builder.cache.reused.set(Tile.get(composition.text),2/* Reused.DOM */),this.tile=builder.run(changes,composition),destroyDropped(oldTile,builder.cache.reused)}// Lock the height during redrawing, since Chrome sometimes
|
|
588
|
+
// messes with the scroll position during DOM mutation (though
|
|
589
|
+
// no relayout is triggered and I cannot imagine how it can
|
|
590
|
+
// recompute the scroll position without a layout)
|
|
591
|
+
this.tile.dom.style.height=this.view.viewState.contentHeight/this.view.scaleY+"px",this.tile.dom.style.flexBasis=this.minWidth?this.minWidth+"px":"";// Chrome will sometimes, when DOM mutations occur directly
|
|
592
|
+
// around the selection, get confused and report a different
|
|
593
|
+
// selection from the one it displays (issue #218). This tries
|
|
594
|
+
// to detect that situation.
|
|
595
|
+
let track=browser.chrome||browser.ios?{node:observer.selectionRange.focusNode,written:!1}:void 0;this.tile.sync(track),track&&(track.written||observer.selectionRange.focusNode!=track.node||!this.tile.dom.contains(track.node))&&(this.forceSelection=!0),this.tile.dom.style.height=""});let gaps=[];if(this.view.viewport.from||this.view.viewport.to<this.view.state.doc.length)for(let child of this.tile.children)child.isWidget()&&child.widget instanceof BlockGapWidget&&gaps.push(child.dom);observer.updateGaps(gaps)}updateEditContextFormatting(update){this.editContextFormatting=this.editContextFormatting.map(update.changes);for(let tr of update.transactions)for(let effect of tr.effects)effect.is(setEditContextFormatting)&&(this.editContextFormatting=effect.value)}// Sync the DOM selection to this.state.selection
|
|
596
|
+
updateSelection(mustRead=!1,fromPointer=!1){(mustRead||!this.view.observer.selectionRange.focusNode)&&this.view.observer.readSelectionRange();let{dom}=this.tile,activeElt=this.view.root.activeElement,focused=activeElt==dom,selectionNotFocus=!focused&&!(this.view.state.facet(editable)||-1<dom.tabIndex)&&hasSelection(dom,this.view.observer.selectionRange)&&!(activeElt&&dom.contains(activeElt));if(!(focused||fromPointer||selectionNotFocus))return;let force=this.forceSelection;this.forceSelection=!1;let main=this.view.state.selection.main,anchor,head;// Always reset on Firefox when next to an uneditable node to
|
|
597
|
+
// avoid invisible cursor bugs (#111)
|
|
598
|
+
if(main.empty?head=anchor=this.inlineDOMNearPos(main.anchor,main.assoc||1):(head=this.inlineDOMNearPos(main.head,main.head==main.from?1:-1),anchor=this.inlineDOMNearPos(main.anchor,main.anchor==main.from?1:-1)),browser.gecko&&main.empty&&!this.hasComposition&&betweenUneditable(anchor)){let dummy=document.createTextNode("");this.view.observer.ignore(()=>anchor.node.insertBefore(dummy,anchor.node.childNodes[anchor.offset]||null)),anchor=head=new DOMPos(dummy,0),force=!0}let domSel=this.view.observer.selectionRange;// If the selection is already here, or in an equivalent position, don't touch it
|
|
599
|
+
!force&&domSel.focusNode&&(isEquivalentPosition(anchor.node,anchor.offset,domSel.anchorNode,domSel.anchorOffset)&&isEquivalentPosition(head.node,head.offset,domSel.focusNode,domSel.focusOffset)||this.suppressWidgetCursorChange(domSel,main))||(this.view.observer.ignore(()=>{browser.android&&browser.chrome&&dom.contains(domSel.focusNode)&&inUneditable(domSel.focusNode,dom)&&(dom.blur(),dom.focus({preventScroll:!0}));let rawSel=getSelection(this.view.root);if(!rawSel);else if(main.empty){// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
600
|
+
if(browser.gecko){let nextTo=nextToUneditable(anchor.node,anchor.offset);if(nextTo&&3/* NextTo.After */!=nextTo){let text=(1==nextTo/* NextTo.Before */?textNodeBefore:textNodeAfter)(anchor.node,anchor.offset);text&&(anchor=new DOMPos(text.node,text.offset))}}rawSel.collapse(anchor.node,anchor.offset),null!=main.bidiLevel&&void 0!==rawSel.caretBidiLevel&&(rawSel.caretBidiLevel=main.bidiLevel)}else if(rawSel.extend){rawSel.collapse(anchor.node,anchor.offset);// Safari will ignore the call above when the editor is
|
|
601
|
+
// hidden, and then raise an error on the call to extend
|
|
602
|
+
// (#940).
|
|
603
|
+
try{rawSel.extend(head.node,head.offset)}catch(_){}}else{// Primitive (IE) way
|
|
604
|
+
let range=document.createRange();main.anchor>main.head&&([anchor,head]=[head,anchor]),range.setEnd(head.node,head.offset),range.setStart(anchor.node,anchor.offset),rawSel.removeAllRanges(),rawSel.addRange(range)}selectionNotFocus&&this.view.root.activeElement==dom&&(dom.blur(),activeElt&&activeElt.focus())}),this.view.observer.setSelectionRange(anchor,head)),this.impreciseAnchor=anchor.precise?null:new DOMPos(domSel.anchorNode,domSel.anchorOffset),this.impreciseHead=head.precise?null:new DOMPos(domSel.focusNode,domSel.focusOffset)}// If a zero-length widget is inserted next to the cursor during
|
|
605
|
+
// composition, avoid moving it across it and disrupting the
|
|
606
|
+
// composition.
|
|
607
|
+
suppressWidgetCursorChange(sel,cursor){return this.hasComposition&&cursor.empty&&isEquivalentPosition(sel.focusNode,sel.focusOffset,sel.anchorNode,sel.anchorOffset)&&this.posFromDOM(sel.focusNode,sel.focusOffset)==cursor.head}enforceCursorAssoc(){if(this.hasComposition)return;let{view}=this,cursor=view.state.selection.main,sel=getSelection(view.root),{anchorNode,anchorOffset}=view.observer.selectionRange;if(sel&&cursor.empty&&cursor.assoc&&sel.modify){let line=this.lineAt(cursor.head,cursor.assoc);if(line){let lineStart=line.posAtStart;if(cursor.head!=lineStart&&cursor.head!=lineStart+line.length){let before=this.coordsAt(cursor.head,-1),after=this.coordsAt(cursor.head,1);if(before&&after&&!(before.bottom>after.top)){let dom=this.domAtPos(cursor.head+cursor.assoc,cursor.assoc);sel.collapse(dom.node,dom.offset),sel.modify("move",0>cursor.assoc?"forward":"backward","lineboundary"),view.observer.readSelectionRange();let newRange=view.observer.selectionRange;view.docView.posFromDOM(newRange.anchorNode,newRange.anchorOffset)!=cursor.from&&sel.collapse(anchorNode,anchorOffset)}}}}}posFromDOM(node,offset){let tile=this.tile.nearest(node);if(!tile)return 2&this.tile.dom.compareDocumentPosition(node)/* PRECEDING */?0:this.view.state.doc.length;let start=tile.posAtStart;if(tile.isComposite()){let after;if(node==tile.dom)after=tile.dom.childNodes[offset];else{let bias=0==maxOffset(node)?0:0==offset?-1:1;for(;;){let parent=node.parentNode;if(parent==tile.dom)break;0==bias&&parent.firstChild!=parent.lastChild&&(node==parent.firstChild?bias=-1:bias=1),node=parent}after=0>bias?node:node.nextSibling}if(after==tile.dom.firstChild)return start;for(;after&&!Tile.get(after);)after=after.nextSibling;if(!after)return start+tile.length;for(let i=0,pos=start,child;;i++){if(child=tile.children[i],child.dom==after)return pos;pos+=child.length+child.breakAfter}}else return tile.isText()?node==tile.dom?start+offset:start+(offset?tile.length:0):start}domAtPos(pos,side){let{tile,offset}=this.tile.resolveBlock(pos,side);return tile.isWidget()?tile.domPosFor(pos,side):tile.domIn(offset,side)}inlineDOMNearPos(pos,side){let beforeOff=-1,beforeBad=!1,afterOff=-1,afterBad=!1,before,after;return(this.tile.blockTiles((tile,off)=>{if(tile.isWidget()){if(32&tile.flags/* TileFlag.After */&&off>=pos)return!0;16&tile.flags/* TileFlag.Before */&&(beforeBad=!0)}else{let end=off+tile.length;if(off<=pos&&(before=tile,beforeOff=pos-off,beforeBad=end<pos),end>=pos&&!after&&(after=tile,afterOff=pos-off,afterBad=off>pos),off>pos&&after)return!0}}),!before&&!after)?this.domAtPos(pos,side):(beforeBad&&after?before=null:afterBad&&before&&(after=null),before&&0>side||!after?before.domIn(beforeOff,side):after.domIn(afterOff,side))}coordsAt(pos,side){let{tile,offset}=this.tile.resolveBlock(pos,side);return tile.isWidget()?tile.widget instanceof BlockGapWidget?null:tile.coordsInWidget(offset,side,!0):tile.coordsIn(offset,side)}lineAt(pos,side){let{tile}=this.tile.resolveBlock(pos,side);return tile.isLine()?tile:null}coordsForChar(pos){function scan(tile,offset){if(tile.isComposite())for(let ch of tile.children){if(ch.length>=offset){let found=scan(ch,offset);if(found)return found}if(offset-=ch.length,0>offset)break}else if(tile.isText()&&offset<tile.length){let end=state.findClusterBreak(tile.text,offset);if(end==offset)return null;let rects=textRange(tile.dom,offset,end).getClientRects();for(let i=0,rect;i<rects.length;i++)if(rect=rects[i],i==rects.length-1||rect.top<rect.bottom&&rect.left<rect.right)return rect}return null}let{tile,offset}=this.tile.resolveBlock(pos,1);return tile.isLine()?scan(tile,offset):null}measureVisibleLineHeights(viewport){let result=[],{from,to}=viewport,contentWidth=this.view.contentDOM.clientWidth,isWider=contentWidth>Math.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,widest=-1,ltr=this.view.textDirection==exports.Direction.LTR,spaceAbove=0,scan=(tile,pos,measureBounds)=>{for(let i=0;i<tile.children.length&&!(pos>to);i++){let child=tile.children[i],end=pos+child.length,childRect=child.dom.getBoundingClientRect(),{height}=childRect;if(measureBounds&&!i&&(spaceAbove+=childRect.top-measureBounds.top),child instanceof BlockWrapperTile)end>from&&scan(child,pos,childRect);else if(pos>=from&&(0<spaceAbove&&result.push(-spaceAbove),result.push(height+spaceAbove),spaceAbove=0,isWider)){let last=child.dom.lastChild,rects=last?clientRectsFor(last):[];if(rects.length){let rect=rects[rects.length-1],width=ltr?rect.right-childRect.left:childRect.right-rect.left;width>widest&&(widest=width,this.minWidth=contentWidth,this.minWidthFrom=pos,this.minWidthTo=end)}}measureBounds&&i==tile.children.length-1&&(spaceAbove+=measureBounds.bottom-childRect.bottom),pos=end+child.breakAfter}};return scan(this.tile,0,null),result}textDirectionAt(pos){let{tile}=this.tile.resolveBlock(pos,1);return"rtl"==getComputedStyle(tile.dom).direction?exports.Direction.RTL:exports.Direction.LTR}measureTextSize(){let lineMeasure=this.tile.blockTiles(tile=>{if(tile.isLine()&&tile.children.length&&20>=tile.length){let totalWidth=0,textHeight;for(let child of tile.children){if(!child.isText()||/[^ -~]/.test(child.text))return;let rects=clientRectsFor(child.dom);if(1!=rects.length)return;totalWidth+=rects[0].width,textHeight=rects[0].height}if(totalWidth)return{lineHeight:tile.dom.getBoundingClientRect().height,charWidth:totalWidth/tile.length,textHeight}}});if(lineMeasure)return lineMeasure;// If no workable line exists, force a layout of a measurable element
|
|
608
|
+
let dummy=document.createElement("div"),lineHeight,charWidth,textHeight;return dummy.className="cm-line",dummy.style.width="99999px",dummy.style.position="absolute",dummy.textContent="abc def ghi jkl mno pqr stu",this.view.observer.ignore(()=>{this.tile.dom.appendChild(dummy);let rect=clientRectsFor(dummy.firstChild)[0];lineHeight=dummy.getBoundingClientRect().height,charWidth=rect&&rect.width?rect.width/27:7,textHeight=rect&&rect.height?rect.height:lineHeight,dummy.remove()}),{lineHeight,charWidth,textHeight}}computeBlockGapDeco(){let deco=[],vs=this.view.viewState;for(let pos=0,i=0;;i++){let next=i==vs.viewports.length?null:vs.viewports[i],end=next?next.from-1:this.view.state.doc.length;if(end>pos){let height=(vs.lineBlockAt(end).bottom-vs.lineBlockAt(pos).top)/this.view.scaleY;deco.push(Decoration.replace({widget:new BlockGapWidget(height),block:!0,inclusive:!0,isBlockGap:!0}).range(pos,end))}if(!next)break;pos=next.to+1}return Decoration.set(deco)}updateDeco(){let i=1,allDeco=this.view.state.facet(decorations).map(d=>{let dynamic=this.dynamicDecorationMap[i++]="function"==typeof d;return dynamic?d(this.view):d}),dynamicOuter=!1,outerDeco=this.view.state.facet(outerDecorations).map((d,i)=>{let dynamic="function"==typeof d;return dynamic&&(dynamicOuter=!0),dynamic?d(this.view):d});for(outerDeco.length&&(this.dynamicDecorationMap[i++]=dynamicOuter,allDeco.push(state.RangeSet.join(outerDeco))),this.decorations=[this.editContextFormatting,...allDeco,this.computeBlockGapDeco(),this.view.viewState.lineGapDeco];i<this.decorations.length;)this.dynamicDecorationMap[i++]=!1;this.blockWrappers=this.view.state.facet(blockWrappers).map(v=>"function"==typeof v?v(this.view):v)}scrollIntoView(target){var _a;if(target.isSnapshot){let ref=this.view.viewState.lineBlockAt(target.range.head);return this.view.scrollDOM.scrollTop=ref.top-target.yMargin,void(this.view.scrollDOM.scrollLeft=target.xMargin)}for(let handler of this.view.state.facet(scrollHandler))try{if(handler(this.view,target.range,target))return!0}catch(e){logException(this.view.state,e,"scroll handler")}let{range}=target,rect=this.coordsAt(range.head,null!==(_a=range.assoc)&&void 0!==_a?_a:range.empty?0:range.head>range.anchor?-1:1),other;if(!rect)return;!range.empty&&(other=this.coordsAt(range.anchor,range.anchor>range.head?-1:1))&&(rect={left:Math.min(rect.left,other.left),top:Math.min(rect.top,other.top),right:Math.max(rect.right,other.right),bottom:Math.max(rect.bottom,other.bottom)});let margins=getScrollMargins(this.view),targetRect={left:rect.left-margins.left,top:rect.top-margins.top,right:rect.right+margins.right,bottom:rect.bottom+margins.bottom},{offsetWidth,offsetHeight}=this.view.scrollDOM;// On mobile browsers, the visual viewport may be smaller than the
|
|
609
|
+
// actual reported viewport, causing scrollRectIntoView to fail to
|
|
610
|
+
// scroll properly. Unfortunately, this visual viewport cannot be
|
|
611
|
+
// updated directly, and scrollIntoView is the only way a script
|
|
612
|
+
// can affect it. So this tries to kludge around the problem by
|
|
613
|
+
// calling scrollIntoView on the scroll target's line.
|
|
614
|
+
if(scrollRectIntoView(this.view.scrollDOM,targetRect,range.head<range.anchor?-1:1,target.x,target.y,Math.max(Math.min(target.xMargin,offsetWidth),-offsetWidth),Math.max(Math.min(target.yMargin,offsetHeight),-offsetHeight),this.view.textDirection==exports.Direction.LTR),window.visualViewport&&1<window.innerHeight-window.visualViewport.height&&(rect.top>window.pageYOffset+window.visualViewport.offsetTop+window.visualViewport.height||rect.bottom<window.pageYOffset+window.visualViewport.offsetTop)){let line=this.view.docView.lineAt(range.head,1);line&&line.dom.scrollIntoView({block:"nearest"})}}lineHasWidget(pos){let scan=child=>child.isWidget()||child.children.some(scan);return scan(this.tile.resolveBlock(pos,1).tile)}destroy(){destroyDropped(this.tile)}}let DecorationComparator$1=class DecorationComparator{constructor(){this.changes=[]}compareRange(from,to){addRange(from,to,this.changes)}comparePoint(from,to){addRange(from,to,this.changes)}boundChange(pos){addRange(pos,pos,this.changes)}};class WrapperComparator{constructor(){this.changes=[]}compareRange(from,to){addRange(from,to,this.changes)}comparePoint(){}boundChange(pos){addRange(pos,pos,this.changes)}}class BlockGapWidget extends WidgetType{constructor(height){super(),this.height=height}toDOM(){let elt=document.createElement("div");return elt.className="cm-gap",this.updateDOM(elt),elt}eq(other){return other.height==this.height}updateDOM(elt){return elt.style.height=this.height+"px",!0}get editable(){return!0}get estimatedHeight(){return this.height}ignoreEvent(){return!1}}class PosAssoc{constructor(pos,assoc){this.pos=pos,this.assoc=assoc}}class InlineCoordsScan{constructor(view,x,y,baseDir){this.view=view,this.x=x,this.y=y,this.baseDir=baseDir,this.line=null,this.spans=null}bidiSpansAt(pos){return(!this.line||this.line.from>pos||this.line.to<pos)&&(this.line=this.view.state.doc.lineAt(pos),this.spans=this.view.bidiSpans(this.line)),this}baseDirAt(pos,side){let{line,spans}=this.bidiSpansAt(pos),level=spans[BidiSpan.find(spans,pos-line.from,-1,side)].level;return level==this.baseDir}dirAt(pos,side){let{line,spans}=this.bidiSpansAt(pos);return spans[BidiSpan.find(spans,pos-line.from,-1,side)].dir}// Used to short-circuit bidi tests for content with a uniform direction
|
|
615
|
+
bidiIn(from,to){let{spans,line}=this.bidiSpansAt(from);return 1<spans.length||spans.length&&(spans[0].level!=this.baseDir||spans[0].to+line.from<to)}// Scan through the rectangles for the content of a tile with inline
|
|
616
|
+
// content, looking for one that overlaps the queried position
|
|
617
|
+
// vertically andis
|
|
618
|
+
// closest horizontally. The caller is responsible for dividing its
|
|
619
|
+
// content into N pieces, and pass an array with N+1 positions
|
|
620
|
+
// (including the position after the last piece). For a text tile,
|
|
621
|
+
// these will be character clusters, for a composite tile, these
|
|
622
|
+
// will be child tiles.
|
|
623
|
+
scan(positions,getRects,recursed=!1){let lo=0,hi=positions.length-1,seen=new Set,bidi=this.bidiIn(positions[0],positions[hi]),closestI=-1,closestDx=1e9,above,below,closestRect;// Because, when the content is bidirectional, a regular binary
|
|
624
|
+
// search is hard to perform (the content order does not
|
|
625
|
+
// correspond to visual order), this loop does something between a
|
|
626
|
+
// regular binary search and a full scan, depending on what it can
|
|
627
|
+
// get away with. The outer hi/lo bounds are only adjusted for
|
|
628
|
+
// elements that are part of the base order.
|
|
629
|
+
//
|
|
630
|
+
// To make sure all elements inside those bounds are visited,
|
|
631
|
+
// eventually, we keep a set of seen indices, and if the midpoint
|
|
632
|
+
// has already been handled, we start in a random index within the
|
|
633
|
+
// current bounds and scan forward until we find an index that
|
|
634
|
+
// hasn't been seen yet.
|
|
635
|
+
search:for(;lo<hi;){let dist=hi-lo,mid=lo+hi>>1;adjust:if(seen.has(mid)){let scan=lo+Math.floor(Math.random()*dist);for(let i=0;i<dist;i++){if(!seen.has(scan)){mid=scan;break adjust}scan++,scan==hi&&(scan=lo)}break search;// No index found, we're done
|
|
636
|
+
}seen.add(mid);let rects=getRects(mid);if(rects)for(let i=0;i<rects.length;i++){let rect=rects[i],side=0;// Ignore empty rectangles when there are other rectangles
|
|
637
|
+
if(!(0==rect.width&&1<rects.length)){if(rect.bottom<this.y)(!above||above.bottom<rect.bottom)&&(above=rect),side=1;else if(rect.top>this.y)(!below||below.top>rect.top)&&(below=rect),side=-1;else{let off=rect.left>this.x?this.x-rect.left:rect.right<this.x?this.x-rect.right:0,dx=Math.abs(off);dx<closestDx&&(closestI=mid,closestDx=dx,closestRect=rect),off&&(side=0>off==(this.baseDir==exports.Direction.LTR)?-1:1)}// Narrow binary search when it is safe to do so
|
|
638
|
+
-1==side&&(!bidi||this.baseDirAt(positions[mid],1))?hi=mid:1==side&&(!bidi||this.baseDirAt(positions[mid+1],-1))&&(lo=mid+1)}}}// If no element with y overlap is found, find the nearest element
|
|
639
|
+
// on the y axis, move this.y into it, and retry the scan.
|
|
640
|
+
if(!closestRect){let side=above&&(!below||this.y-above.bottom<below.top-this.y)?above:below;return this.y=(side.top+side.bottom)/2,this.scan(positions,getRects,!0)}// Handle the case where closest matched a higher element on the
|
|
641
|
+
// same line as an element below/above the coords
|
|
642
|
+
if(closestDx&&!recursed){let{top,bottom}=closestRect;if(above&&above.bottom>(top+top+bottom)/3)return this.y=above.bottom-1,this.scan(positions,getRects,!0);if(below&&below.top<(top+bottom+bottom)/3)return this.y=below.top+1,this.scan(positions,getRects,!0)}let ltr=(bidi?this.dirAt(positions[closestI],1):this.baseDir)==exports.Direction.LTR;return{i:closestI,// Test whether x is closes to the start or end of this element
|
|
643
|
+
after:this.x>(closestRect.left+closestRect.right)/2==ltr}}scanText(tile,offset){let positions=[];for(let i=0;i<tile.length;i=state.findClusterBreak(tile.text,i))positions.push(offset+i);positions.push(offset+tile.length);let scan=this.scan(positions,i=>{let off=positions[i]-offset,end=positions[i+1]-offset;return textRange(tile.dom,off,end).getClientRects()});return scan.after?new PosAssoc(positions[scan.i+1],-1):new PosAssoc(positions[scan.i],1)}scanTile(tile,offset){if(!tile.length)return new PosAssoc(offset,1);if(1==tile.children.length){// Short-circuit single-child tiles
|
|
644
|
+
let child=tile.children[0];if(child.isText())return this.scanText(child,offset);if(child.isComposite())return this.scanTile(child,offset)}let positions=[offset];for(let i=0,pos=offset;i<tile.children.length;i++)positions.push(pos+=tile.children[i].length);let scan=this.scan(positions,i=>{let child=tile.children[i];return 48&child.flags/* TileFlag.PointWidget */?null:(1==child.dom.nodeType?child.dom:textRange(child.dom,0,child.length)).getClientRects()}),child=tile.children[scan.i],pos=positions[scan.i];return child.isText()?this.scanText(child,pos):child.isComposite()?this.scanTile(child,pos):scan.after?new PosAssoc(positions[scan.i+1],-1):new PosAssoc(pos,1)}}const LineBreakPlaceholder="\uFFFF";class DOMReader{constructor(points,view){this.points=points,this.view=view,this.text="",this.lineSeparator=view.state.facet(state.EditorState.lineSeparator)}append(text){this.text+=text}lineBreak(){this.text+=LineBreakPlaceholder}readRange(start,end){if(!start)return this;let parent=start.parentNode;for(let cur=start;;){this.findPointBefore(parent,cur);let oldLen=this.text.length;this.readNode(cur);let tile=Tile.get(cur),next=cur.nextSibling;if(next==end){(null===tile||void 0===tile?void 0:tile.breakAfter)&&!next&&parent!=this.view.contentDOM&&this.lineBreak();break}let nextTile=Tile.get(next);(tile&&nextTile?tile.breakAfter:(tile?tile.breakAfter:isBlockElement(cur))||isBlockElement(next)&&("BR"!=cur.nodeName||(null===tile||void 0===tile?void 0:tile.isWidget()))&&this.text.length>oldLen)&&!isEmptyToEnd(next,end)&&this.lineBreak(),cur=next}return this.findPointBefore(parent,end),this}readTextNode(node){let text=node.nodeValue;for(let point of this.points)point.node==node&&(point.pos=this.text.length+Math.min(point.offset,text.length));for(let off=0,re=this.lineSeparator?null:/\r\n?|\n/g;;){let nextBreak=-1,breakSize=1,m;if(this.lineSeparator?(nextBreak=text.indexOf(this.lineSeparator,off),breakSize=this.lineSeparator.length):(m=re.exec(text))&&(nextBreak=m.index,breakSize=m[0].length),this.append(text.slice(off,0>nextBreak?text.length:nextBreak)),0>nextBreak)break;if(this.lineBreak(),1<breakSize)for(let point of this.points)point.node==node&&point.pos>this.text.length&&(point.pos-=breakSize-1);off=nextBreak+breakSize}}readNode(node){let tile=Tile.get(node),fromView=tile&&tile.overrideDOMText;if(null!=fromView){this.findPointInside(node,fromView.length);for(let i=fromView.iter();!i.next().done;)i.lineBreak?this.lineBreak():this.append(i.value)}else 3==node.nodeType?this.readTextNode(node):"BR"==node.nodeName?node.nextSibling&&this.lineBreak():1==node.nodeType&&this.readRange(node.firstChild,null)}findPointBefore(node,next){for(let point of this.points)point.node==node&&node.childNodes[point.offset]==next&&(point.pos=this.text.length)}findPointInside(node,length){for(let point of this.points)(3==node.nodeType?point.node==node:node.contains(point.node))&&(point.pos=this.text.length+(isAtEnd(node,point.node,point.offset)?length:0))}}class DOMPoint{constructor(node,offset){this.node=node,this.offset=offset,this.pos=-1}}class DOMChange{constructor(view,start,end,typeOver){this.typeOver=typeOver,this.bounds=null,this.text="",this.domChanged=-1<start;let{impreciseHead:iHead,impreciseAnchor:iAnchor}=view.docView,curSel=view.state.selection;if(view.state.readOnly&&-1<start)this.newSel=null;else if(-1<start&&(this.bounds=domBoundsAround(view.docView.tile,start,end,0))){let selPoints=iHead||iAnchor?[]:selectionPoints(view),reader=new DOMReader(selPoints,view);reader.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=reader.text,this.newSel=selectionFromPoints(selPoints,this.bounds.from)}else{let domSel=view.observer.selectionRange,head=iHead&&iHead.node==domSel.focusNode&&iHead.offset==domSel.focusOffset||!contains(view.contentDOM,domSel.focusNode)?curSel.main.head:view.docView.posFromDOM(domSel.focusNode,domSel.focusOffset),anchor=iAnchor&&iAnchor.node==domSel.anchorNode&&iAnchor.offset==domSel.anchorOffset||!contains(view.contentDOM,domSel.anchorNode)?curSel.main.anchor:view.docView.posFromDOM(domSel.anchorNode,domSel.anchorOffset),vp=view.viewport;// iOS will refuse to select the block gaps when doing
|
|
645
|
+
// select-all.
|
|
646
|
+
// Chrome will put the selection *inside* them, confusing
|
|
647
|
+
// posFromDOM
|
|
648
|
+
if((browser.ios||browser.chrome)&&curSel.main.empty&&head!=anchor&&(0<vp.from||vp.to<view.state.doc.length)){let from=Math.min(head,anchor),to=Math.max(head,anchor),offFrom=vp.from-from,offTo=vp.to-to;(0==offFrom||1==offFrom||0==from)&&(0==offTo||-1==offTo||to==view.state.doc.length)&&(head=0,anchor=view.state.doc.length)}if(-1<view.inputState.composing&&1<curSel.ranges.length)this.newSel=curSel.replaceRange(state.EditorSelection.range(anchor,head));else if(view.lineWrapping&&anchor==head&&!(curSel.main.empty&&curSel.main.head==head)&&view.inputState.lastTouchTime>Date.now()-100){// If this is a cursor selection change in a line-wrapping
|
|
649
|
+
// editor that may have been a touch, use the last touch
|
|
650
|
+
// position to assign a side to the cursor.
|
|
651
|
+
let before=view.coordsAtPos(head,-1),assoc=0;before&&(assoc=view.inputState.lastTouchY<=before.bottom?-1:1),this.newSel=state.EditorSelection.create([state.EditorSelection.cursor(head,assoc)])}else this.newSel=state.EditorSelection.single(anchor,head)}}}class InputState{setSelectionOrigin(origin){this.lastSelectionOrigin=origin,this.lastSelectionTime=Date.now()}constructor(view){this.view=view,this.lastKeyCode=0,this.lastKeyTime=0,this.lastTouchTime=0,this.lastTouchX=0,this.lastTouchY=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.lastWheelEvent=0,this.pendingIOSKey=void 0,this.tabFocusMode=-1,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastContextMenu=0,this.scrollHandlers=[],this.handlers=Object.create(null),this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.compositionPendingKey=!1,this.compositionPendingChange=!1,this.insertingText="",this.insertingTextAt=0,this.mouseSelection=null,this.draggedContent=null,this.handleEvent=this.handleEvent.bind(this),this.notifiedFocused=view.hasFocus,browser.safari&&view.contentDOM.addEventListener("input",()=>null),browser.gecko&&firefoxCopyCutHack(view.contentDOM.ownerDocument)}handleEvent(event){!eventBelongsToEditor(this.view,event)||this.ignoreDuringComposition(event)||"keydown"==event.type&&this.keydown(event)||(0==this.view.updateState/* UpdateState.Idle */?this.runHandlers(event.type,event):Promise.resolve().then(()=>this.runHandlers(event.type,event)))}runHandlers(type,event){let handlers=this.handlers[type];if(handlers){for(let observer of handlers.observers)observer(this.view,event);for(let handler of handlers.handlers){if(event.defaultPrevented)break;if(handler(this.view,event)){event.preventDefault();break}}}}ensureHandlers(plugins){let handlers=computeHandlers(plugins),prev=this.handlers,dom=this.view.contentDOM;for(let type in handlers)if("scroll"!=type){let passive=!handlers[type].handlers.length,exists=prev[type];exists&&passive!=!exists.handlers.length&&(dom.removeEventListener(type,this.handleEvent),exists=null),exists||dom.addEventListener(type,this.handleEvent,{passive})}for(let type in prev)"scroll"==type||handlers[type]||dom.removeEventListener(type,this.handleEvent);this.handlers=handlers}keydown(event){if(this.lastKeyCode=event.keyCode,this.lastKeyTime=Date.now(),9==event.keyCode&&-1<this.tabFocusMode&&(!this.tabFocusMode||Date.now()<=this.tabFocusMode))return!0;// Chrome for Android usually doesn't fire proper key events, but
|
|
652
|
+
// occasionally does, usually surrounded by a bunch of complicated
|
|
653
|
+
// composition changes. When an enter or backspace key event is
|
|
654
|
+
// seen, hold off on handling DOM events for a bit, and then
|
|
655
|
+
// dispatch it.
|
|
656
|
+
if(0<this.tabFocusMode&&27!=event.keyCode&&0>modifierCodes.indexOf(event.keyCode)&&(this.tabFocusMode=-1),browser.android&&browser.chrome&&!event.synthetic&&(13==event.keyCode||8==event.keyCode))return this.view.observer.delayAndroidKey(event.key,event.keyCode),!0;// Preventing the default behavior of Enter on iOS makes the
|
|
657
|
+
// virtual keyboard get stuck in the wrong (lowercase)
|
|
658
|
+
// state. So we let it go through, and then, in
|
|
659
|
+
// applyDOMChange, notify key handlers of it and reset to
|
|
660
|
+
// the state they produce.
|
|
661
|
+
let pending;return!browser.ios||event.synthetic||event.altKey||event.metaKey||event.shiftKey||(!(pending=PendingKeys.find(key=>key.keyCode==event.keyCode))||event.ctrlKey)&&(!(-1<EmacsyPendingKeys.indexOf(event.key))||!event.ctrlKey)?(229!=event.keyCode&&this.view.observer.forceFlush(),!1):(this.pendingIOSKey=pending||event,setTimeout(()=>this.flushIOSKey(),250),!0)}flushIOSKey(change){let key=this.pendingIOSKey;return!!key&&!("Enter"==key.key&&change&&change.from<change.to&&/^\S+$/.test(change.insert.toString()))&&(this.pendingIOSKey=void 0,dispatchKey(this.view.contentDOM,key.key,key.keyCode,key instanceof KeyboardEvent?key:void 0));// This looks like an autocorrection before Enter
|
|
662
|
+
}ignoreDuringComposition(event){return /^key/.test(event.type)&&!event.synthetic&&(!!(0<this.composing)||!!(browser.safari&&!browser.ios&&this.compositionPendingKey&&100>Date.now()-this.compositionEndedAt)&&(this.compositionPendingKey=!1,!0));// See https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/.
|
|
663
|
+
// On some input method editors (IMEs), the Enter key is used to
|
|
664
|
+
// confirm character selection. On Safari, when Enter is pressed,
|
|
665
|
+
// compositionend and keydown events are sometimes emitted in the
|
|
666
|
+
// wrong order. The key event should still be ignored, even when
|
|
667
|
+
// it happens after the compositionend event.
|
|
668
|
+
}startMouseSelection(mouseSelection){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=mouseSelection}update(update){this.view.observer.update(update),this.mouseSelection&&this.mouseSelection.update(update),this.draggedContent&&update.docChanged&&(this.draggedContent=this.draggedContent.map(update.changes)),update.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}const PendingKeys=[{key:"Backspace",keyCode:8,inputType:"deleteContentBackward"},{key:"Enter",keyCode:13,inputType:"insertParagraph"},{key:"Enter",keyCode:13,inputType:"insertLineBreak"},{key:"Delete",keyCode:46,inputType:"deleteContentForward"}],EmacsyPendingKeys="dthko",modifierCodes=[16,17,18,20,91,92,224,225],dragScrollMargin=6;// Key codes for modifier keys
|
|
669
|
+
class MouseSelection{constructor(view,startEvent,style,mustSelect){this.view=view,this.startEvent=startEvent,this.style=style,this.mustSelect=mustSelect,this.scrollSpeed={x:0,y:0},this.scrolling=-1,this.lastEvent=startEvent,this.scrollParents=scrollableParents(view.contentDOM),this.atoms=view.state.facet(atomicRanges).map(f=>f(view));let doc=view.contentDOM.ownerDocument;doc.addEventListener("mousemove",this.move=this.move.bind(this)),doc.addEventListener("mouseup",this.up=this.up.bind(this)),this.extend=startEvent.shiftKey,this.multiple=view.state.facet(state.EditorState.allowMultipleSelections)&&addsSelectionRange(view,startEvent),this.dragging=!!(isInPrimarySelection(view,startEvent)&&1==getClickType(startEvent))&&null}start(event){!1===this.dragging&&this.select(event)}move(event){if(0==event.buttons)return this.destroy();if(this.dragging||null==this.dragging&&10>dist(this.startEvent,event))return;this.select(this.lastEvent=event);let sx=0,sy=0,left=0,top=0,right=this.view.win.innerWidth,bottom=this.view.win.innerHeight;this.scrollParents.x&&({left,right}=this.scrollParents.x.getBoundingClientRect()),this.scrollParents.y&&({top,bottom}=this.scrollParents.y.getBoundingClientRect());let margins=getScrollMargins(this.view);event.clientX-margins.left<=left+dragScrollMargin?sx=-dragScrollSpeed(left-event.clientX):event.clientX+margins.right>=right-dragScrollMargin&&(sx=dragScrollSpeed(event.clientX-right)),event.clientY-margins.top<=top+dragScrollMargin?sy=-dragScrollSpeed(top-event.clientY):event.clientY+margins.bottom>=bottom-dragScrollMargin&&(sy=dragScrollSpeed(event.clientY-bottom)),this.setScrollSpeed(sx,sy)}up(event){null==this.dragging&&this.select(this.lastEvent),this.dragging||event.preventDefault(),this.destroy()}destroy(){this.setScrollSpeed(0,0);let doc=this.view.contentDOM.ownerDocument;doc.removeEventListener("mousemove",this.move),doc.removeEventListener("mouseup",this.up),this.view.inputState.mouseSelection=this.view.inputState.draggedContent=null}setScrollSpeed(sx,sy){this.scrollSpeed={x:sx,y:sy},sx||sy?0>this.scrolling&&(this.scrolling=setInterval(()=>this.scroll(),50)):-1<this.scrolling&&(clearInterval(this.scrolling),this.scrolling=-1)}scroll(){let{x,y}=this.scrollSpeed;x&&this.scrollParents.x&&(this.scrollParents.x.scrollLeft+=x,x=0),y&&this.scrollParents.y&&(this.scrollParents.y.scrollTop+=y,y=0),(x||y)&&this.view.win.scrollBy(x,y),!1===this.dragging&&this.select(this.lastEvent)}select(event){let{view}=this,selection=skipAtomsForSelection(this.atoms,this.style.get(event,this.extend,this.multiple));(this.mustSelect||!selection.eq(view.state.selection,!1===this.dragging))&&this.view.dispatch({selection,userEvent:"select.pointer"}),this.mustSelect=!1}update(update){update.transactions.some(tr=>tr.isUserEvent("input.type"))?this.destroy():this.style.update(update)&&setTimeout(()=>this.select(this.lastEvent),20)}}const handlers=Object.create(null),observers=Object.create(null),brokenClipboardAPI=browser.ie&&15>browser.ie_version||browser.ios&&604>browser.webkit_version;// This is very crude, but unfortunately both these browsers _pretend_
|
|
670
|
+
// that they have a clipboard API—all the objects and methods are
|
|
671
|
+
// there, they just don't work, and they are hard to test.
|
|
672
|
+
observers.scroll=view=>{view.inputState.lastScrollTop=view.scrollDOM.scrollTop,view.inputState.lastScrollLeft=view.scrollDOM.scrollLeft},observers.wheel=observers.mousewheel=view=>{view.inputState.lastWheelEvent=Date.now()},handlers.keydown=(view,event)=>(view.inputState.setSelectionOrigin("select"),27==event.keyCode&&0!=view.inputState.tabFocusMode&&(view.inputState.tabFocusMode=Date.now()+2e3),!1),observers.touchstart=(view,e)=>{let iState=view.inputState,touch=e.targetTouches[0];iState.lastTouchTime=Date.now(),touch&&(iState.lastTouchX=touch.clientX,iState.lastTouchY=touch.clientY),iState.setSelectionOrigin("select.pointer")},observers.touchmove=view=>{view.inputState.setSelectionOrigin("select.pointer")},handlers.mousedown=(view,event)=>{if(view.observer.flush(),view.inputState.lastTouchTime>Date.now()-2e3)return!1;// Ignore touch interaction
|
|
673
|
+
let style=null;for(let makeStyle of view.state.facet(mouseSelectionStyle))if(style=makeStyle(view,event),style)break;if(style||0!=event.button||(style=basicMouseSelection(view,event)),style){let mustFocus=!view.hasFocus;view.inputState.startMouseSelection(new MouseSelection(view,event,style,mustFocus)),mustFocus&&view.observer.ignore(()=>{focusPreventScroll(view.contentDOM);let active=view.root.activeElement;active&&!active.contains(view.contentDOM)&&active.blur()});let mouseSel=view.inputState.mouseSelection;if(mouseSel)return mouseSel.start(event),!1===mouseSel.dragging}else view.inputState.setSelectionOrigin("select.pointer");return!1};const BadMouseDetail=browser.ie&&11>=browser.ie_version;let lastMouseDown=null,lastMouseDownCount=0,lastMouseDownTime=0;handlers.dragstart=(view,event)=>{let{selection:{main:range}}=view.state;if(event.target.draggable){let tile=view.docView.tile.nearest(event.target);if(tile&&tile.isWidget()){let from=tile.posAtStart,to=from+tile.length;(from>=range.to||to<=range.from)&&(range=state.EditorSelection.range(from,to))}}let{inputState}=view;return inputState.mouseSelection&&(inputState.mouseSelection.dragging=!0),inputState.draggedContent=range,event.dataTransfer&&(event.dataTransfer.setData("Text",textFilter(view.state,clipboardOutputFilter,view.state.sliceDoc(range.from,range.to))),event.dataTransfer.effectAllowed="copyMove"),!1},handlers.dragend=view=>(view.inputState.draggedContent=null,!1),handlers.drop=(view,event)=>{if(!event.dataTransfer)return!1;if(view.state.readOnly)return!0;let files=event.dataTransfer.files;if(files&&files.length){// For a file drop, read the file's text.
|
|
674
|
+
let text=Array(files.length),read=0,finishFile=()=>{++read==files.length&&dropText(view,event,text.filter(s=>null!=s).join(view.state.lineBreak),!1)};for(let i=0,reader;i<files.length;i++)reader=new FileReader,reader.onerror=finishFile,reader.onload=()=>{/[\x00-\x08\x0e-\x1f]{2}/.test(reader.result)||(text[i]=reader.result),finishFile()},reader.readAsText(files[i]);return!0}else{let text=event.dataTransfer.getData("Text");if(text)return dropText(view,event,text,!0),!0}return!1},handlers.paste=(view,event)=>{if(view.state.readOnly)return!0;view.observer.flush();let data=brokenClipboardAPI?null:event.clipboardData;return data?(doPaste(view,data.getData("text/plain")||data.getData("text/uri-list")),!0):(capturePaste(view),!1)};let lastLinewiseCopy=null;handlers.copy=handlers.cut=(view,event)=>{// If the DOM selection is outside this editor, don't intercept.
|
|
675
|
+
// This happens when a parent editor (like ProseMirror) selects content that
|
|
676
|
+
// spans multiple elements including this CodeMirror. The copy event may
|
|
677
|
+
// bubble through CodeMirror (e.g. when CodeMirror is the first or the last
|
|
678
|
+
// element in the selection), but we should let the parent handle it.
|
|
679
|
+
if(!hasSelection(view.contentDOM,view.observer.selectionRange))return!1;let{text,ranges,linewise}=copiedRange(view.state);if(!text&&!linewise)return!1;lastLinewiseCopy=linewise?text:null,"cut"!=event.type||view.state.readOnly||view.dispatch({changes:ranges,scrollIntoView:!0,userEvent:"delete.cut"});let data=brokenClipboardAPI?null:event.clipboardData;return data?(data.clearData(),data.setData("text/plain",text),!0):(captureCopy(view,text),!1)};const isFocusChange=state.Annotation.define();observers.focus=view=>{view.inputState.lastFocusTime=Date.now(),!view.scrollDOM.scrollTop&&(view.inputState.lastScrollTop||view.inputState.lastScrollLeft)&&(view.scrollDOM.scrollTop=view.inputState.lastScrollTop,view.scrollDOM.scrollLeft=view.inputState.lastScrollLeft),updateForFocusChange(view)},observers.blur=view=>{view.observer.clearSelectionRange(),updateForFocusChange(view)},observers.compositionstart=observers.compositionupdate=view=>{view.observer.editContext||(null==view.inputState.compositionFirstChange&&(view.inputState.compositionFirstChange=!0),0>view.inputState.composing&&(view.inputState.composing=0))},observers.compositionend=view=>{view.observer.editContext||(// Composition handled by edit context
|
|
680
|
+
view.inputState.composing=-1,view.inputState.compositionEndedAt=Date.now(),view.inputState.compositionPendingKey=!0,view.inputState.compositionPendingChange=0<view.observer.pendingRecords().length,view.inputState.compositionFirstChange=null,browser.chrome&&browser.android?view.observer.flushSoon():view.inputState.compositionPendingChange?Promise.resolve().then(()=>view.observer.flush()):setTimeout(()=>{0>view.inputState.composing&&view.docView.hasComposition&&view.update([])},50))},observers.contextmenu=view=>{view.inputState.lastContextMenu=Date.now()},handlers.beforeinput=(view,event)=>{var _a,_b;// In EditContext mode, we must handle insertReplacementText events
|
|
681
|
+
// directly, to make spell checking corrections work
|
|
682
|
+
if(("insertText"==event.inputType||"insertCompositionText"==event.inputType)&&(view.inputState.insertingText=event.data,view.inputState.insertingTextAt=Date.now()),"insertReplacementText"==event.inputType&&view.observer.editContext){let text=null===(_a=event.dataTransfer)||void 0===_a?void 0:_a.getData("text/plain"),ranges=event.getTargetRanges();if(text&&ranges.length){let r=ranges[0],from=view.posAtDOM(r.startContainer,r.startOffset),to=view.posAtDOM(r.endContainer,r.endOffset);return applyDOMChangeInner(view,{from,to,insert:view.state.toText(text)},null),!0}}// Because Chrome Android doesn't fire useful key events, use
|
|
683
|
+
// beforeinput to detect backspace (and possibly enter and delete,
|
|
684
|
+
// but those usually don't even seem to fire beforeinput events at
|
|
685
|
+
// the moment) and fake a key event for it.
|
|
686
|
+
//
|
|
687
|
+
// (preventDefault on beforeinput, though supported in the spec,
|
|
688
|
+
// seems to do nothing at all on Chrome).
|
|
689
|
+
let pending;if(browser.chrome&&browser.android&&(pending=PendingKeys.find(key=>key.inputType==event.inputType))&&(view.observer.delayAndroidKey(pending.key,pending.keyCode),"Backspace"==pending.key||"Delete"==pending.key)){let startViewHeight=(null===(_b=window.visualViewport)||void 0===_b?void 0:_b.height)||0;setTimeout(()=>{var _a;// Backspacing near uneditable nodes on Chrome Android sometimes
|
|
690
|
+
// closes the virtual keyboard. This tries to crudely detect
|
|
691
|
+
// that and refocus to get it back.
|
|
692
|
+
((null===(_a=window.visualViewport)||void 0===_a?void 0:_a.height)||0)>startViewHeight+10&&view.hasFocus&&(view.contentDOM.blur(),view.focus())},100)}return browser.ios&&"deleteContentForward"==event.inputType&&view.observer.flushSoon(),browser.safari&&"insertText"==event.inputType&&0<=view.inputState.composing&&setTimeout(()=>observers.compositionend(view,event),20),!1};const appliedFirefoxHack=new Set,wrappingWhiteSpace=["pre-wrap","normal","pre-line","break-spaces"];// Used to track, during updateHeight, if any actual heights changed
|
|
693
|
+
let heightChangeFlag=!1;class HeightOracle{constructor(lineWrapping){this.lineWrapping=lineWrapping,this.doc=state.Text.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.textHeight=14,this.lineLength=30}heightForGap(from,to){let lines=this.doc.lineAt(to).number-this.doc.lineAt(from).number+1;return this.lineWrapping&&(lines+=Math.max(0,Math.ceil((to-from-.5*(lines*this.lineLength))/this.lineLength))),this.lineHeight*lines}heightForLine(length){if(!this.lineWrapping)return this.lineHeight;let lines=1+Math.max(0,Math.ceil((length-this.lineLength)/Math.max(1,this.lineLength-5)));return lines*this.lineHeight}setDoc(doc){return this.doc=doc,this}mustRefreshForWrapping(whiteSpace){return-1<wrappingWhiteSpace.indexOf(whiteSpace)!=this.lineWrapping}mustRefreshForHeights(lineHeights){let newHeight=!1;for(let i=0,h;i<lineHeights.length;i++)h=lineHeights[i],0>h?i++:!this.heightSamples[Math.floor(10*h)]&&(newHeight=!0,this.heightSamples[Math.floor(10*h)]=!0);return newHeight}refresh(whiteSpace,lineHeight,charWidth,textHeight,lineLength,knownHeights){let lineWrapping=-1<wrappingWhiteSpace.indexOf(whiteSpace),changed=.3<Math.abs(lineHeight-this.lineHeight)||this.lineWrapping!=lineWrapping;if(this.lineWrapping=lineWrapping,this.lineHeight=lineHeight,this.charWidth=charWidth,this.textHeight=textHeight,this.lineLength=lineLength,changed){this.heightSamples={};for(let i=0,h;i<knownHeights.length;i++)h=knownHeights[i],0>h?i++:this.heightSamples[Math.floor(10*h)]=!0}return changed}}// This object is used by `updateHeight` to make DOM measurements
|
|
694
|
+
// arrive at the right nodes. The `heights` array is a sequence of
|
|
695
|
+
// block heights, starting from position `from`.
|
|
696
|
+
class MeasuredHeights{constructor(from,heights){this.from=from,this.heights=heights,this.index=0}get more(){return this.index<this.heights.length}}/**
|
|
697
|
+
Record used to represent information about a block-level element
|
|
698
|
+
in the editor view.
|
|
699
|
+
*/class BlockInfo{/**
|
|
700
|
+
@internal
|
|
701
|
+
*/constructor(/**
|
|
702
|
+
The start of the element in the document.
|
|
703
|
+
*/from,/**
|
|
704
|
+
The length of the element.
|
|
705
|
+
*/length,/**
|
|
706
|
+
The top position of the element (relative to the top of the
|
|
707
|
+
document).
|
|
708
|
+
*/top,/**
|
|
709
|
+
Its height.
|
|
710
|
+
*/height,/**
|
|
711
|
+
@internal Weird packed field that holds an array of children
|
|
712
|
+
for composite blocks, a decoration for block widgets, and a
|
|
713
|
+
number indicating the amount of widget-created line breaks for
|
|
714
|
+
text blocks.
|
|
715
|
+
*/_content){this.from=from,this.length=length,this.top=top,this.height=height,this._content=_content}/**
|
|
716
|
+
The type of element this is. When querying lines, this may be
|
|
717
|
+
an array of all the blocks that make up the line.
|
|
718
|
+
*/get type(){return"number"==typeof this._content?exports.BlockType.Text:Array.isArray(this._content)?this._content:this._content.type}/**
|
|
719
|
+
The end of the element as a document position.
|
|
720
|
+
*/get to(){return this.from+this.length}/**
|
|
721
|
+
The bottom position of the element.
|
|
722
|
+
*/get bottom(){return this.top+this.height}/**
|
|
723
|
+
If this is a widget block, this will return the widget
|
|
724
|
+
associated with it.
|
|
725
|
+
*/get widget(){return this._content instanceof PointDecoration?this._content.widget:null}/**
|
|
726
|
+
If this is a textblock, this holds the number of line breaks
|
|
727
|
+
that appear in widgets inside the block.
|
|
728
|
+
*/get widgetLineBreaks(){return"number"==typeof this._content?this._content:0}/**
|
|
729
|
+
@internal
|
|
730
|
+
*/join(other){let content=(Array.isArray(this._content)?this._content:[this]).concat(Array.isArray(other._content)?other._content:[other]);return new BlockInfo(this.from,this.length+other.length,this.top,this.height+other.height,content)}}var QueryType;(function(QueryType){QueryType[QueryType.ByPos=0]="ByPos",QueryType[QueryType.ByHeight=1]="ByHeight",QueryType[QueryType.ByPosNoHeight=2]="ByPosNoHeight"})(QueryType||(QueryType={}));const Epsilon=1e-3;class HeightMap{constructor(length,// The number of characters covered
|
|
731
|
+
height,// Height of this part of the document
|
|
732
|
+
flags=2/* Flag.Outdated */){this.length=length,this.height=height,this.flags=flags}get outdated(){return 0<(2&this.flags/* Flag.Outdated */)}set outdated(value){this.flags=(value?2/* Flag.Outdated */:0)|-3&this.flags/* Flag.Outdated */}setHeight(height){this.height!=height&&(Math.abs(this.height-height)>Epsilon&&(heightChangeFlag=!0),this.height=height)}// Base case is to replace a leaf node, which simply builds a tree
|
|
733
|
+
// from the new nodes and returns that (HeightMapBranch and
|
|
734
|
+
// HeightMapGap override this to actually use from/to)
|
|
735
|
+
replace(_from,_to,nodes){return HeightMap.of(nodes)}// Again, these are base cases, and are overridden for branch and gap nodes.
|
|
736
|
+
decomposeLeft(_to,result){result.push(this)}decomposeRight(_from,result){result.push(this)}applyChanges(decorations,oldDoc,oracle,changes){let me=this,doc=oracle.doc;for(let i=changes.length-1;0<=i;i--){let{fromA,toA,fromB,toB}=changes[i],start=me.lineAt(fromA,QueryType.ByPosNoHeight,oracle.setDoc(oldDoc),0,0),end=start.to>=toA?start:me.lineAt(toA,QueryType.ByPosNoHeight,oracle,0,0);for(toB+=end.to-toA,toA=end.to;0<i&&start.from<=changes[i-1].toA;)fromA=changes[i-1].fromA,fromB=changes[i-1].fromB,i--,fromA<start.from&&(start=me.lineAt(fromA,QueryType.ByPosNoHeight,oracle,0,0));fromB+=start.from-fromA,fromA=start.from;let nodes=NodeBuilder.build(oracle.setDoc(doc),decorations,fromB,toB);me=replace(me,me.replace(fromA,toA,nodes))}return me.updateHeight(oracle,0)}static empty(){return new HeightMapText(0,0,0)}// nodes uses null values to indicate the position of line breaks.
|
|
737
|
+
// There are never line breaks at the start or end of the array, or
|
|
738
|
+
// two line breaks next to each other, and the array isn't allowed
|
|
739
|
+
// to be empty (same restrictions as return value from the builder).
|
|
740
|
+
static of(nodes){if(1==nodes.length)return nodes[0];let i=0,j=nodes.length,before=0,after=0;for(;;)if(i==j){if(before>2*after){let split=nodes[i-1];split.break?nodes.splice(--i,1,split.left,null,split.right):nodes.splice(--i,1,split.left,split.right),j+=1+split.break,before-=split.size}else if(after>2*before){let split=nodes[j];split.break?nodes.splice(j,1,split.left,null,split.right):nodes.splice(j,1,split.left,split.right),j+=2+split.break,after-=split.size}else break;}else if(before<after){let next=nodes[i++];next&&(before+=next.size)}else{let next=nodes[--j];next&&(after+=next.size)}let brk=0;return null==nodes[i-1]?(brk=1,i--):null==nodes[i]&&(brk=1,j++),new HeightMapBranch(HeightMap.of(nodes.slice(0,i)),brk,HeightMap.of(nodes.slice(j)))}}HeightMap.prototype.size=1;const SpaceDeco=Decoration.replace({});class HeightMapBlock extends HeightMap{constructor(length,height,deco){super(length,height),this.deco=deco,this.spaceAbove=0}mainBlock(top,offset){return new BlockInfo(offset,this.length,top+this.spaceAbove,this.height-this.spaceAbove,this.deco||0)}blockAt(height,_oracle,top,offset){return this.spaceAbove&&height<top+this.spaceAbove?new BlockInfo(offset,0,top,this.spaceAbove,SpaceDeco):this.mainBlock(top,offset)}lineAt(_value,_type,oracle,top,offset){let main=this.mainBlock(top,offset);return this.spaceAbove?this.blockAt(0,oracle,top,offset).join(main):main}forEachLine(from,to,oracle,top,offset,f){from<=offset+this.length&&to>=offset&&f(this.lineAt(0,QueryType.ByPos,oracle,top,offset))}setMeasuredHeight(measured){let next=measured.heights[measured.index++];0>next?(this.spaceAbove=-next,next=measured.heights[measured.index++]):this.spaceAbove=0,this.setHeight(next)}updateHeight(oracle,offset=0,_force=!1,measured){return measured&&measured.from<=offset&&measured.more&&this.setMeasuredHeight(measured),this.outdated=!1,this}toString(){return`block(${this.length})`}}class HeightMapText extends HeightMapBlock{constructor(length,height,above){super(length,height,null),this.collapsed=0,this.widgetHeight=0,this.breaks=0,this.spaceAbove=above}mainBlock(top,offset){return new BlockInfo(offset,this.length,top+this.spaceAbove,this.height-this.spaceAbove,this.breaks)}replace(_from,_to,nodes){let node=nodes[0];return 1==nodes.length&&(node instanceof HeightMapText||node instanceof HeightMapGap&&4&node.flags/* Flag.SingleLine */)&&10>Math.abs(this.length-node.length)?(node instanceof HeightMapGap?node=new HeightMapText(node.length,this.height,this.spaceAbove):node.height=this.height,this.outdated||(node.outdated=!1),node):HeightMap.of(nodes)}updateHeight(oracle,offset=0,force=!1,measured){return measured&&measured.from<=offset&&measured.more?this.setMeasuredHeight(measured):(force||this.outdated)&&(this.spaceAbove=0,this.setHeight(Math.max(this.widgetHeight,oracle.heightForLine(this.length-this.collapsed))+this.breaks*oracle.lineHeight)),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:""}${this.widgetHeight?":"+this.widgetHeight:""})`}}class HeightMapGap extends HeightMap{constructor(length){super(length,0)}heightMetrics(oracle,offset){let firstLine=oracle.doc.lineAt(offset).number,lastLine=oracle.doc.lineAt(offset+this.length).number,lines=lastLine-firstLine+1,perChar=0,perLine;if(oracle.lineWrapping){let totalPerLine=Math.min(this.height,oracle.lineHeight*lines);perLine=totalPerLine/lines,this.length>lines+1&&(perChar=(this.height-totalPerLine)/(this.length-lines-1))}else perLine=this.height/lines;return{firstLine,lastLine,perLine,perChar}}blockAt(height,oracle,top,offset){let{firstLine,lastLine,perLine,perChar}=this.heightMetrics(oracle,offset);if(oracle.lineWrapping){let guess=offset+(height<oracle.lineHeight?0:Math.round(Math.max(0,Math.min(1,(height-top)/this.height))*this.length)),line=oracle.doc.lineAt(guess),lineHeight=perLine+line.length*perChar,lineTop=Math.max(top,height-lineHeight/2);return new BlockInfo(line.from,line.length,lineTop,lineHeight,0)}else{let line=Math.max(0,Math.min(lastLine-firstLine,Math.floor((height-top)/perLine))),{from,length}=oracle.doc.line(firstLine+line);return new BlockInfo(from,length,top+perLine*line,perLine,0)}}lineAt(value,type,oracle,top,offset){if(type==QueryType.ByHeight)return this.blockAt(value,oracle,top,offset);if(type==QueryType.ByPosNoHeight){let{from,to}=oracle.doc.lineAt(value);return new BlockInfo(from,to-from,0,0,0)}let{firstLine,perLine,perChar}=this.heightMetrics(oracle,offset),line=oracle.doc.lineAt(value),lineHeight=perLine+line.length*perChar,linesAbove=line.number-firstLine,lineTop=top+perLine*linesAbove+perChar*(line.from-offset-linesAbove);return new BlockInfo(line.from,line.length,Math.max(top,Math.min(lineTop,top+this.height-lineHeight)),lineHeight,0)}forEachLine(from,to,oracle,top,offset,f){from=Math.max(from,offset),to=Math.min(to,offset+this.length);let{firstLine,perLine,perChar}=this.heightMetrics(oracle,offset);for(let pos=from,lineTop=top,line;pos<=to;){if(line=oracle.doc.lineAt(pos),pos==from){let linesAbove=line.number-firstLine;lineTop+=perLine*linesAbove+perChar*(from-offset-linesAbove)}let lineHeight=perLine+perChar*line.length;f(new BlockInfo(line.from,line.length,lineTop,lineHeight,0)),lineTop+=lineHeight,pos=line.to+1}}replace(from,to,nodes){let after=this.length-to;if(0<after){let last=nodes[nodes.length-1];last instanceof HeightMapGap?nodes[nodes.length-1]=new HeightMapGap(last.length+after):nodes.push(null,new HeightMapGap(after-1))}if(0<from){let first=nodes[0];first instanceof HeightMapGap?nodes[0]=new HeightMapGap(from+first.length):nodes.unshift(new HeightMapGap(from-1),null)}return HeightMap.of(nodes)}decomposeLeft(to,result){result.push(new HeightMapGap(to-1),null)}decomposeRight(from,result){result.push(null,new HeightMapGap(this.length-from-1))}updateHeight(oracle,offset=0,force=!1,measured){let end=offset+this.length;if(measured&&measured.from<=offset+this.length&&measured.more){// Fill in part of this gap with measured lines. We know there
|
|
741
|
+
// can't be widgets or collapsed ranges in those lines, because
|
|
742
|
+
// they would already have been added to the heightmap (gaps
|
|
743
|
+
// only contain plain text).
|
|
744
|
+
let nodes=[],pos=Math.max(offset,measured.from),singleHeight=-1;for(measured.from>offset&&nodes.push(new HeightMapGap(measured.from-offset-1).updateHeight(oracle,offset));pos<=end&&measured.more;){let len=oracle.doc.lineAt(pos).length;nodes.length&&nodes.push(null);let height=measured.heights[measured.index++],above=0;0>height&&(above=-height,height=measured.heights[measured.index++]),-1==singleHeight?singleHeight=height:Math.abs(height-singleHeight)>=Epsilon&&(singleHeight=-2);let line=new HeightMapText(len,height,above);line.outdated=!1,nodes.push(line),pos+=len+1}pos<=end&&nodes.push(null,new HeightMapGap(end-pos).updateHeight(oracle,pos));let result=HeightMap.of(nodes);return(0>singleHeight||Math.abs(result.height-this.height)>=Epsilon||Math.abs(singleHeight-this.heightMetrics(oracle,offset).perLine)>=Epsilon)&&(heightChangeFlag=!0),replace(this,result)}return(force||this.outdated)&&(this.setHeight(oracle.heightForGap(offset,offset+this.length)),this.outdated=!1),this}toString(){return`gap(${this.length})`}}class HeightMapBranch extends HeightMap{constructor(left,brk,right){super(left.length+brk+right.length,left.height+right.height,brk|(left.outdated||right.outdated?2/* Flag.Outdated */:0)),this.left=left,this.right=right,this.size=left.size+right.size}get break(){return 1&this.flags/* Flag.Break */}blockAt(height,oracle,top,offset){let mid=top+this.left.height;return height<mid?this.left.blockAt(height,oracle,top,offset):this.right.blockAt(height,oracle,mid,offset+this.left.length+this.break)}lineAt(value,type,oracle,top,offset){let rightTop=top+this.left.height,rightOffset=offset+this.left.length+this.break,left=type==QueryType.ByHeight?value<rightTop:value<rightOffset,base=left?this.left.lineAt(value,type,oracle,top,offset):this.right.lineAt(value,type,oracle,rightTop,rightOffset);if(this.break||(left?base.to<rightOffset:base.from>rightOffset))return base;let subQuery=type==QueryType.ByPosNoHeight?QueryType.ByPosNoHeight:QueryType.ByPos;return left?base.join(this.right.lineAt(rightOffset,subQuery,oracle,rightTop,rightOffset)):this.left.lineAt(rightOffset,subQuery,oracle,top,offset).join(base)}forEachLine(from,to,oracle,top,offset,f){let rightTop=top+this.left.height,rightOffset=offset+this.left.length+this.break;if(this.break)from<rightOffset&&this.left.forEachLine(from,to,oracle,top,offset,f),to>=rightOffset&&this.right.forEachLine(from,to,oracle,rightTop,rightOffset,f);else{let mid=this.lineAt(rightOffset,QueryType.ByPos,oracle,top,offset);from<mid.from&&this.left.forEachLine(from,mid.from-1,oracle,top,offset,f),mid.to>=from&&mid.from<=to&&f(mid),to>mid.to&&this.right.forEachLine(mid.to+1,to,oracle,rightTop,rightOffset,f)}}replace(from,to,nodes){let rightStart=this.left.length+this.break;if(to<rightStart)return this.balanced(this.left.replace(from,to,nodes),this.right);if(from>this.left.length)return this.balanced(this.left,this.right.replace(from-rightStart,to-rightStart,nodes));let result=[];0<from&&this.decomposeLeft(from,result);let left=result.length;for(let node of nodes)result.push(node);if(0<from&&mergeGaps(result,left-1),to<this.length){let right=result.length;this.decomposeRight(to,result),mergeGaps(result,right)}return HeightMap.of(result)}decomposeLeft(to,result){let left=this.left.length;return to<=left?this.left.decomposeLeft(to,result):void(result.push(this.left),this.break&&(left++,to>=left&&result.push(null)),to>left&&this.right.decomposeLeft(to-left,result))}decomposeRight(from,result){let left=this.left.length,right=left+this.break;return from>=right?this.right.decomposeRight(from-right,result):void(from<left&&this.left.decomposeRight(from,result),this.break&&from<right&&result.push(null),result.push(this.right))}balanced(left,right){return left.size>2*right.size||right.size>2*left.size?HeightMap.of(this.break?[left,null,right]:[left,right]):(this.left=replace(this.left,left),this.right=replace(this.right,right),this.setHeight(left.height+right.height),this.outdated=left.outdated||right.outdated,this.size=left.size+right.size,this.length=left.length+this.break+right.length,this)}updateHeight(oracle,offset=0,force=!1,measured){let{left,right}=this,rightStart=offset+left.length+this.break,rebalance=null;return(measured&&measured.from<=offset+left.length&&measured.more?rebalance=left=left.updateHeight(oracle,offset,force,measured):left.updateHeight(oracle,offset,force),measured&&measured.from<=rightStart+right.length&&measured.more?rebalance=right=right.updateHeight(oracle,rightStart,force,measured):right.updateHeight(oracle,rightStart,force),rebalance)?this.balanced(left,right):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?" ":"-")+this.right}}const relevantWidgetHeight=5;class NodeBuilder{constructor(pos,oracle){this.pos=pos,this.oracle=oracle,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=pos}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(_from,to){if(-1<this.lineStart){let end=Math.min(to,this.lineEnd),last=this.nodes[this.nodes.length-1];last instanceof HeightMapText?last.length+=end-this.pos:(end>this.pos||!this.isCovered)&&this.nodes.push(new HeightMapText(end-this.pos,-1,0)),this.writtenTo=end,to>end&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=to}point(from,to,deco){if(from<to||deco.heightRelevant){let height=deco.widget?deco.widget.estimatedHeight:0,breaks=deco.widget?deco.widget.lineBreaks:0;0>height&&(height=this.oracle.lineHeight);let len=to-from;deco.block?this.addBlock(new HeightMapBlock(len,height,deco)):(len||breaks||height>=relevantWidgetHeight)&&this.addLineDeco(height,breaks,len)}else to>from&&this.span(from,to);-1<this.lineEnd&&this.lineEnd<this.pos&&(this.lineEnd=this.oracle.doc.lineAt(this.pos).to)}enterLine(){if(!(-1<this.lineStart)){let{from,to}=this.oracle.doc.lineAt(this.pos);this.lineStart=from,this.lineEnd=to,this.writtenTo<from&&((this.writtenTo<from-1||null==this.nodes[this.nodes.length-1])&&this.nodes.push(this.blankContent(this.writtenTo,from-1)),this.nodes.push(null)),this.pos>from&&this.nodes.push(new HeightMapText(this.pos-from,-1,0)),this.writtenTo=this.pos}}blankContent(from,to){let gap=new HeightMapGap(to-from);return this.oracle.doc.lineAt(from).to==to&&(gap.flags|=4/* Flag.SingleLine */),gap}ensureLine(){this.enterLine();let last=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(last instanceof HeightMapText)return last;let line=new HeightMapText(0,-1,0);return this.nodes.push(line),line}addBlock(block){this.enterLine();let deco=block.deco;deco&&0<deco.startSide&&!this.isCovered&&this.ensureLine(),this.nodes.push(block),this.writtenTo=this.pos+=block.length,deco&&0<deco.endSide&&(this.covering=block)}addLineDeco(height,breaks,length){let line=this.ensureLine();line.length+=length,line.collapsed+=length,line.widgetHeight=Math.max(line.widgetHeight,height),line.breaks+=breaks,this.writtenTo=this.pos+=length}finish(from){let last=0==this.nodes.length?null:this.nodes[this.nodes.length-1];!(-1<this.lineStart)||last instanceof HeightMapText||this.isCovered?(this.writtenTo<this.pos||null==last)&&this.nodes.push(this.blankContent(this.writtenTo,this.pos)):this.nodes.push(new HeightMapText(0,-1,0));let pos=from;for(let node of this.nodes)node instanceof HeightMapText&&node.updateHeight(this.oracle,pos),pos+=node?node.length:1;return this.nodes}// Always called with a region that on both sides either stretches
|
|
745
|
+
// to a line break or the end of the document.
|
|
746
|
+
// The returned array uses null to indicate line breaks, but never
|
|
747
|
+
// starts or ends in a line break, or has multiple line breaks next
|
|
748
|
+
// to each other.
|
|
749
|
+
static build(oracle,decorations,from,to){let builder=new NodeBuilder(from,oracle);return state.RangeSet.spans(decorations,from,to,builder,0),builder.finish(from)}}class DecorationComparator{constructor(){this.changes=[]}compareRange(){}comparePoint(from,to,a,b){(from<to||a&&a.heightRelevant||b&&b.heightRelevant)&&addRange(from,to,this.changes,5)}}class LineGap{constructor(from,to,size,displaySize){this.from=from,this.to=to,this.size=size,this.displaySize=displaySize}static same(a,b){if(a.length!=b.length)return!1;for(let i=0;i<a.length;i++){let gA=a[i],gB=b[i];if(gA.from!=gB.from||gA.to!=gB.to||gA.size!=gB.size)return!1}return!0}draw(viewState,wrapping){return Decoration.replace({widget:new LineGapWidget(this.displaySize*(wrapping?viewState.scaleY:viewState.scaleX),wrapping)}).range(this.from,this.to)}}class LineGapWidget extends WidgetType{constructor(size,vertical){super(),this.size=size,this.vertical=vertical}eq(other){return other.size==this.size&&other.vertical==this.vertical}toDOM(){let elt=document.createElement("div");return this.vertical?elt.style.height=this.size+"px":(elt.style.width=this.size+"px",elt.style.height="2px",elt.style.display="inline-block"),elt}get estimatedHeight(){return this.vertical?this.size:-1}}class ViewState{constructor(view,state$1){this.view=view,this.state=state$1,this.pixelViewport={left:0,right:window.innerWidth,top:0,bottom:0},this.inView=!0,this.paddingTop=0,this.paddingBottom=0,this.contentDOMWidth=0,this.contentDOMHeight=0,this.editorHeight=0,this.editorWidth=0,this.scaleX=1,this.scaleY=1,this.scrollOffset=0,this.scrolledToBottom=!1,this.scrollAnchorPos=0,this.scrollAnchorHeight=-1,this.scaler=IdScaler,this.scrollTarget=null,this.printing=!1,this.mustMeasureContent=!0,this.defaultTextDirection=exports.Direction.LTR,this.visibleRanges=[],this.mustEnforceCursorAssoc=!1;let guessWrapping=state$1.facet(contentAttributes).some(v=>"function"!=typeof v&&"cm-lineWrapping"==v.class);this.heightOracle=new HeightOracle(guessWrapping),this.stateDeco=staticDeco(state$1),this.heightMap=HeightMap.empty().applyChanges(this.stateDeco,state.Text.empty,this.heightOracle.setDoc(state$1.doc),[new ChangedRange(0,0,0,state$1.doc.length)]);for(let i=0;2>i&&(this.viewport=this.getViewport(0,null),!!this.updateForViewport());i++);this.updateViewportLines(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=Decoration.set(this.lineGaps.map(gap=>gap.draw(this,!1))),this.scrollParent=view.scrollDOM,this.computeVisibleRanges()}updateForViewport(){let viewports=[this.viewport],{main}=this.state.selection;for(let i=0,pos;1>=i;i++)if(pos=i?main.head:main.anchor,!viewports.some(({from,to})=>pos>=from&&pos<=to)){let{from,to}=this.lineBlockAt(pos);viewports.push(new Viewport(from,to))}return this.viewports=viewports.sort((a,b)=>a.from-b.from),this.updateScaler()}updateScaler(){let scaler=this.scaler;return this.scaler=7e6>=this.heightMap.height/* VP.MaxDOMHeight */?IdScaler:new BigScaler(this.heightOracle,this.heightMap,this.viewports),scaler.eq(this.scaler)?0:2/* UpdateFlag.Height */}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.heightOracle.setDoc(this.state.doc),0,0,block=>{this.viewportLines.push(scaleBlock(block,this.scaler))})}update(update,scrollTarget=null){this.state=update.state;let prevDeco=this.stateDeco;this.stateDeco=staticDeco(this.state);let contentChanges=update.changedRanges,heightChanges=ChangedRange.extendWithRanges(contentChanges,heightRelevantDecoChanges(prevDeco,this.stateDeco,update?update.changes:state.ChangeSet.empty(this.state.doc.length))),prevHeight=this.heightMap.height,scrollAnchor=this.scrolledToBottom?null:this.scrollAnchorAt(this.scrollOffset);clearHeightChangeFlag(),this.heightMap=this.heightMap.applyChanges(this.stateDeco,update.startState.doc,this.heightOracle.setDoc(this.state.doc),heightChanges),(this.heightMap.height!=prevHeight||heightChangeFlag)&&(update.flags|=2/* UpdateFlag.Height */),scrollAnchor?(this.scrollAnchorPos=update.changes.mapPos(scrollAnchor.from,-1),this.scrollAnchorHeight=scrollAnchor.top):(this.scrollAnchorPos=-1,this.scrollAnchorHeight=prevHeight);let viewport=heightChanges.length?this.mapViewport(this.viewport,update.changes):this.viewport;(scrollTarget&&(scrollTarget.range.head<viewport.from||scrollTarget.range.head>viewport.to)||!this.viewportIsAppropriate(viewport))&&(viewport=this.getViewport(0,scrollTarget));let viewportChange=viewport.from!=this.viewport.from||viewport.to!=this.viewport.to;this.viewport=viewport,update.flags|=this.updateForViewport(),(viewportChange||!update.changes.empty||2&update.flags/* UpdateFlag.Height */)&&this.updateViewportLines(),(this.lineGaps.length||4000<this.viewport.to-this.viewport.from)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,update.changes))),update.flags|=this.computeVisibleRanges(update.changes),scrollTarget&&(this.scrollTarget=scrollTarget),!this.mustEnforceCursorAssoc&&(update.selectionSet||update.focusChanged)&&update.view.lineWrapping&&update.state.selection.main.empty&&update.state.selection.main.assoc&&!update.state.facet(nativeSelectionHidden)&&(this.mustEnforceCursorAssoc=!0)}measure(){let{view}=this,dom=view.contentDOM,style=window.getComputedStyle(dom),oracle=this.heightOracle,whiteSpace=style.whiteSpace;this.defaultTextDirection="rtl"==style.direction?exports.Direction.RTL:exports.Direction.LTR;let refresh=this.heightOracle.mustRefreshForWrapping(whiteSpace)||"refresh"===this.mustMeasureContent,domRect=dom.getBoundingClientRect(),measureContent=refresh||this.mustMeasureContent||this.contentDOMHeight!=domRect.height;this.contentDOMHeight=domRect.height,this.mustMeasureContent=!1;let result=0,bias=0;if(domRect.width&&domRect.height){let{scaleX,scaleY}=getScale(dom,domRect);(.005<scaleX&&.005<Math.abs(this.scaleX-scaleX)||.005<scaleY&&.005<Math.abs(this.scaleY-scaleY))&&(this.scaleX=scaleX,this.scaleY=scaleY,result|=16/* UpdateFlag.Geometry */,refresh=measureContent=!0)}// Vertical padding
|
|
750
|
+
let paddingTop=(parseInt(style.paddingTop)||0)*this.scaleY,paddingBottom=(parseInt(style.paddingBottom)||0)*this.scaleY;(this.paddingTop!=paddingTop||this.paddingBottom!=paddingBottom)&&(this.paddingTop=paddingTop,this.paddingBottom=paddingBottom,result|=18/* UpdateFlag.Height */),this.editorWidth!=view.scrollDOM.clientWidth&&(oracle.lineWrapping&&(measureContent=!0),this.editorWidth=view.scrollDOM.clientWidth,result|=16/* UpdateFlag.Geometry */);let scrollParent=scrollableParents(this.view.contentDOM,!1).y;scrollParent!=this.scrollParent&&(this.scrollParent=scrollParent,this.scrollAnchorHeight=-1,this.scrollOffset=0);let scrollOffset=this.getScrollOffset();this.scrollOffset!=scrollOffset&&(this.scrollAnchorHeight=-1,this.scrollOffset=scrollOffset),this.scrolledToBottom=isScrolledToBottom(this.scrollParent||view.win);// Pixel viewport
|
|
751
|
+
let pixelViewport=(this.printing?fullPixelRange:visiblePixelRange)(dom,this.paddingTop),dTop=pixelViewport.top-this.pixelViewport.top,dBottom=pixelViewport.bottom-this.pixelViewport.bottom;this.pixelViewport=pixelViewport;let inView=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(inView!=this.inView&&(this.inView=inView,inView&&(measureContent=!0)),!this.inView&&!this.scrollTarget&&!inWindow(view.dom))return 0;let contentWidth=domRect.width;if((this.contentDOMWidth!=contentWidth||this.editorHeight!=view.scrollDOM.clientHeight)&&(this.contentDOMWidth=domRect.width,this.editorHeight=view.scrollDOM.clientHeight,result|=16/* UpdateFlag.Geometry */),measureContent){let lineHeights=view.docView.measureVisibleLineHeights(this.viewport);if(oracle.mustRefreshForHeights(lineHeights)&&(refresh=!0),refresh||oracle.lineWrapping&&Math.abs(contentWidth-this.contentDOMWidth)>oracle.charWidth){let{lineHeight,charWidth,textHeight}=view.docView.measureTextSize();refresh=0<lineHeight&&oracle.refresh(whiteSpace,lineHeight,charWidth,textHeight,Math.max(5,contentWidth/charWidth),lineHeights),refresh&&(view.docView.minWidth=0,result|=16/* UpdateFlag.Geometry */)}0<dTop&&0<dBottom?bias=Math.max(dTop,dBottom):0>dTop&&0>dBottom&&(bias=Math.min(dTop,dBottom)),clearHeightChangeFlag();for(let vp of this.viewports){let heights=vp.from==this.viewport.from?lineHeights:view.docView.measureVisibleLineHeights(vp);this.heightMap=(refresh?HeightMap.empty().applyChanges(this.stateDeco,state.Text.empty,this.heightOracle,[new ChangedRange(0,0,0,view.state.doc.length)]):this.heightMap).updateHeight(oracle,0,refresh,new MeasuredHeights(vp.from,heights))}heightChangeFlag&&(result|=2/* UpdateFlag.Height */)}let viewportChange=!this.viewportIsAppropriate(this.viewport,bias)||this.scrollTarget&&(this.scrollTarget.range.head<this.viewport.from||this.scrollTarget.range.head>this.viewport.to);return viewportChange&&(2&result/* UpdateFlag.Height */&&(result|=this.updateScaler()),this.viewport=this.getViewport(bias,this.scrollTarget),result|=this.updateForViewport()),(2&result/* UpdateFlag.Height */||viewportChange)&&this.updateViewportLines(),(this.lineGaps.length||4000<this.viewport.to-this.viewport.from)&&this.updateLineGaps(this.ensureLineGaps(refresh?[]:this.lineGaps,view)),result|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,view.docView.enforceCursorAssoc()),result}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(bias,scrollTarget){// This will divide VP.Margin between the top and the
|
|
752
|
+
// bottom, depending on the bias (the change in viewport position
|
|
753
|
+
// since the last update). It'll hold a number between 0 and 1
|
|
754
|
+
let marginTop=.5-Math.max(-.5,Math.min(.5,bias/1e3/* VP.Margin *//2)),map=this.heightMap,oracle=this.heightOracle,{visibleTop,visibleBottom}=this,viewport=new Viewport(map.lineAt(visibleTop-1e3*marginTop/* VP.Margin */,QueryType.ByHeight,oracle,0,0).from,map.lineAt(visibleBottom+1e3*(1-marginTop)/* VP.Margin */,QueryType.ByHeight,oracle,0,0).to);// If scrollTarget is given, make sure the viewport includes that position
|
|
755
|
+
if(scrollTarget){let{head}=scrollTarget.range;if(head<viewport.from||head>viewport.to){let viewHeight=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),block=map.lineAt(head,QueryType.ByPos,oracle,0,0),topPos;topPos="center"==scrollTarget.y?(block.top+block.bottom)/2-viewHeight/2:"start"==scrollTarget.y||"nearest"==scrollTarget.y&&head<viewport.from?block.top:block.bottom-viewHeight,viewport=new Viewport(map.lineAt(topPos-500,QueryType.ByHeight,oracle,0,0).from,map.lineAt(topPos+viewHeight+500,QueryType.ByHeight,oracle,0,0).to)}}return viewport}mapViewport(viewport,changes){let from=changes.mapPos(viewport.from,-1),to=changes.mapPos(viewport.to,1);return new Viewport(this.heightMap.lineAt(from,QueryType.ByPos,this.heightOracle,0,0).from,this.heightMap.lineAt(to,QueryType.ByPos,this.heightOracle,0,0).to)}// Checks if a given viewport covers the visible part of the
|
|
756
|
+
// document and not too much beyond that.
|
|
757
|
+
viewportIsAppropriate({from,to},bias=0){if(!this.inView)return!0;let{top}=this.heightMap.lineAt(from,QueryType.ByPos,this.heightOracle,0,0),{bottom}=this.heightMap.lineAt(to,QueryType.ByPos,this.heightOracle,0,0),{visibleTop,visibleBottom}=this;return(0==from||top<=visibleTop-Math.max(10/* VP.MinCoverMargin */,Math.min(-bias,250/* VP.MaxCoverMargin */)))&&(to==this.state.doc.length||bottom>=visibleBottom+Math.max(10/* VP.MinCoverMargin */,Math.min(bias,250/* VP.MaxCoverMargin */)))&&top>visibleTop-2000/* VP.Margin */&&bottom<visibleBottom+2000/* VP.Margin */}mapLineGaps(gaps,changes){if(!gaps.length||changes.empty)return gaps;let mapped=[];for(let gap of gaps)changes.touchesRange(gap.from,gap.to)||mapped.push(new LineGap(changes.mapPos(gap.from),changes.mapPos(gap.to),gap.size,gap.displaySize));return mapped}// Computes positions in the viewport where the start or end of a
|
|
758
|
+
// line should be hidden, trying to reuse existing line gaps when
|
|
759
|
+
// appropriate to avoid unneccesary redraws.
|
|
760
|
+
// Uses crude character-counting for the positioning and sizing,
|
|
761
|
+
// since actual DOM coordinates aren't always available and
|
|
762
|
+
// predictable. Relies on generous margins (see LG.Margin) to hide
|
|
763
|
+
// the artifacts this might produce from the user.
|
|
764
|
+
ensureLineGaps(current,mayMeasure){let wrapping=this.heightOracle.lineWrapping,margin=wrapping?1e4/* LG.MarginWrap */:2e3/* LG.Margin */,halfMargin=margin>>1,doubleMargin=margin<<1;// The non-wrapping logic won't work at all in predominantly right-to-left text.
|
|
765
|
+
if(this.defaultTextDirection!=exports.Direction.LTR&&!wrapping)return[];let gaps=[],addGap=(from,to,line,structure)=>{if(to-from<halfMargin)return;let sel=this.state.selection.main,avoid=[sel.from];sel.empty||avoid.push(sel.to);for(let pos of avoid)if(pos>from&&pos<to)return addGap(from,pos-10/* LG.SelectionMargin */,line,structure),void addGap(pos+10/* LG.SelectionMargin */,to,line,structure);let gap=find(current,gap=>gap.from>=line.from&&gap.to<=line.to&&Math.abs(gap.from-from)<halfMargin&&Math.abs(gap.to-to)<halfMargin&&!avoid.some(pos=>gap.from<pos&&gap.to>pos));if(!gap){// When scrolling down, snap gap ends to line starts to avoid shifts in wrapping
|
|
766
|
+
if(to<line.to&&mayMeasure&&wrapping&&mayMeasure.visibleRanges.some(r=>r.from<=to&&r.to>=to)){let lineStart=mayMeasure.moveToLineBoundary(state.EditorSelection.cursor(to),!1,!0).head;lineStart>from&&(to=lineStart)}let size=this.gapSize(line,from,to,structure),displaySize=wrapping||2e6>size/* VP.MaxHorizGap */?size:2e6/* VP.MaxHorizGap */;gap=new LineGap(from,to,size,displaySize)}gaps.push(gap)},checkLine=line=>{if(line.length<doubleMargin||line.type!=exports.BlockType.Text)return;let structure=lineStructure(line.from,line.to,this.stateDeco);if(structure.total<doubleMargin)return;let target=this.scrollTarget?this.scrollTarget.range.head:null,viewFrom,viewTo;if(wrapping){let marginHeight=margin/this.heightOracle.lineLength*this.heightOracle.lineHeight,top,bot;if(null!=target){let targetFrac=findFraction(structure,target),spaceFrac=((this.visibleBottom-this.visibleTop)/2+marginHeight)/line.height;top=targetFrac-spaceFrac,bot=targetFrac+spaceFrac}else top=(this.visibleTop-line.top-marginHeight)/line.height,bot=(this.visibleBottom-line.top+marginHeight)/line.height;viewFrom=findPosition(structure,top),viewTo=findPosition(structure,bot)}else{let totalWidth=structure.total*this.heightOracle.charWidth,marginWidth=margin*this.heightOracle.charWidth,horizOffset=0;if(2e6<totalWidth/* VP.MaxHorizGap */)for(let old of current)old.from>=line.from&&old.from<line.to&&old.size!=old.displaySize&&old.from*this.heightOracle.charWidth+horizOffset<this.pixelViewport.left&&(horizOffset=old.size-old.displaySize);let pxLeft=this.pixelViewport.left+horizOffset,pxRight=this.pixelViewport.right+horizOffset,left,right;if(null!=target){let targetFrac=findFraction(structure,target),spaceFrac=((pxRight-pxLeft)/2+marginWidth)/totalWidth;left=targetFrac-spaceFrac,right=targetFrac+spaceFrac}else left=(pxLeft-marginWidth)/totalWidth,right=(pxRight+marginWidth)/totalWidth;viewFrom=findPosition(structure,left),viewTo=findPosition(structure,right)}viewFrom>line.from&&addGap(line.from,viewFrom,line,structure),viewTo<line.to&&addGap(viewTo,line.to,line,structure)};for(let line of this.viewportLines)Array.isArray(line.type)?line.type.forEach(checkLine):checkLine(line);return gaps}gapSize(line,from,to,structure){let fraction=findFraction(structure,to)-findFraction(structure,from);return this.heightOracle.lineWrapping?line.height*fraction:structure.total*this.heightOracle.charWidth*fraction}updateLineGaps(gaps){LineGap.same(gaps,this.lineGaps)||(this.lineGaps=gaps,this.lineGapDeco=Decoration.set(gaps.map(gap=>gap.draw(this,this.heightOracle.lineWrapping))))}computeVisibleRanges(changes){let deco=this.stateDeco;this.lineGaps.length&&(deco=deco.concat(this.lineGapDeco));let ranges=[];state.RangeSet.spans(deco,this.viewport.from,this.viewport.to,{span(from,to){ranges.push({from,to})},point(){}},20);let changed=0;if(ranges.length!=this.visibleRanges.length)changed=12/* UpdateFlag.Viewport */;else for(let i=0;i<ranges.length&&!(8&changed/* UpdateFlag.ViewportMoved */);i++){let old=this.visibleRanges[i],nw=ranges[i];(old.from!=nw.from||old.to!=nw.to)&&(changed|=4/* UpdateFlag.Viewport */,!(changes&&changes.mapPos(old.from,-1)==nw.from&&changes.mapPos(old.to,1)==nw.to)&&(changed|=8/* UpdateFlag.ViewportMoved */))}return this.visibleRanges=ranges,changed}lineBlockAt(pos){return pos>=this.viewport.from&&pos<=this.viewport.to&&this.viewportLines.find(b=>b.from<=pos&&b.to>=pos)||scaleBlock(this.heightMap.lineAt(pos,QueryType.ByPos,this.heightOracle,0,0),this.scaler)}lineBlockAtHeight(height){return height>=this.viewportLines[0].top&&height<=this.viewportLines[this.viewportLines.length-1].bottom&&this.viewportLines.find(l=>l.top<=height&&l.bottom>=height)||scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height),QueryType.ByHeight,this.heightOracle,0,0),this.scaler)}getScrollOffset(){let base=this.scrollParent==this.view.scrollDOM?this.scrollParent.scrollTop:(this.scrollParent?this.scrollParent.getBoundingClientRect().top:0)-this.view.contentDOM.getBoundingClientRect().top;return base*this.scaleY}scrollAnchorAt(scrollOffset){let block=this.lineBlockAtHeight(scrollOffset+8);return block.from>=this.viewport.from||200<this.viewportLines[0].top-scrollOffset?block:this.viewportLines[0]}elementAtHeight(height){return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height),this.heightOracle,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class Viewport{constructor(from,to){this.from=from,this.to=to}}const IdScaler={toDOM(n){return n},fromDOM(n){return n},scale:1,eq(other){return other==this}};class BigScaler{constructor(oracle,heightMap,viewports){let vpHeight=0,base=0,domBase=0;this.viewports=viewports.map(({from,to})=>{let top=heightMap.lineAt(from,QueryType.ByPos,oracle,0,0).top,bottom=heightMap.lineAt(to,QueryType.ByPos,oracle,0,0).bottom;return vpHeight+=bottom-top,{from,to,top,bottom,domTop:0,domBottom:0}}),this.scale=(7e6/* VP.MaxDOMHeight */-vpHeight)/(heightMap.height-vpHeight);for(let obj of this.viewports)obj.domTop=domBase+(obj.top-base)*this.scale,domBase=obj.domBottom=obj.domTop+(obj.bottom-obj.top),base=obj.bottom}toDOM(n){for(let i=0,base=0,domBase=0,vp;;i++){if(vp=i<this.viewports.length?this.viewports[i]:null,!vp||n<vp.top)return domBase+(n-base)*this.scale;if(n<=vp.bottom)return vp.domTop+(n-vp.top);base=vp.bottom,domBase=vp.domBottom}}fromDOM(n){for(let i=0,base=0,domBase=0,vp;;i++){if(vp=i<this.viewports.length?this.viewports[i]:null,!vp||n<vp.domTop)return base+(n-domBase)/this.scale;if(n<=vp.domBottom)return vp.top+(n-vp.domTop);base=vp.bottom,domBase=vp.domBottom}}eq(other){return!!(other instanceof BigScaler)&&this.scale==other.scale&&this.viewports.length==other.viewports.length&&this.viewports.every((vp,i)=>vp.from==other.viewports[i].from&&vp.to==other.viewports[i].to)}}const theme=state.Facet.define({combine:strs=>strs.join(" ")}),darkTheme=state.Facet.define({combine:values=>-1<values.indexOf(!0)}),baseThemeID=styleMod.StyleModule.newName(),baseLightID=styleMod.StyleModule.newName(),baseDarkID=styleMod.StyleModule.newName(),lightDarkIDs={"&light":"."+baseLightID,"&dark":"."+baseDarkID},baseTheme$1=buildTheme("."+baseThemeID,{"&":{position:"relative !important",boxSizing:"border-box","&.cm-focused":{// Provide a simple default outline to make sure a focused
|
|
767
|
+
// editor is visually distinct. Can't leave the default behavior
|
|
768
|
+
// because that will apply to the content element, which is
|
|
769
|
+
// inside the scrollable container and doesn't include the
|
|
770
|
+
// gutters. We also can't use an 'auto' outline, since those
|
|
771
|
+
// are, for some reason, drawn behind the element content, which
|
|
772
|
+
// will cause things like the active line background to cover
|
|
773
|
+
// the outline (#297).
|
|
774
|
+
outline:"1px dotted #212121"},display:"flex !important",flexDirection:"column"},".cm-scroller":{display:"flex !important",alignItems:"flex-start !important",fontFamily:"monospace",lineHeight:1.4,height:"100%",overflowX:"auto",position:"relative",zIndex:0,overflowAnchor:"none"},".cm-content":{margin:0,flexGrow:2,flexShrink:0,display:"block",whiteSpace:"pre",wordWrap:"normal",// Issue #456
|
|
775
|
+
boxSizing:"border-box",minHeight:"100%",padding:"4px 0",outline:"none","&[contenteditable=true]":{WebkitUserModify:"read-write-plaintext-only"}},".cm-lineWrapping":{whiteSpace_fallback:"pre-wrap",// For IE
|
|
776
|
+
whiteSpace:"break-spaces",wordBreak:"break-word",// For Safari, which doesn't support overflow-wrap: anywhere
|
|
777
|
+
overflowWrap:"anywhere",flexShrink:1},"&light .cm-content":{caretColor:"black"},"&dark .cm-content":{caretColor:"white"},".cm-line":{display:"block",padding:"0 2px 0 6px"},".cm-layer":{position:"absolute",left:0,top:0,contain:"size style","& > *":{position:"absolute"}},"&light .cm-selectionBackground":{background:"#d9d9d9"},"&dark .cm-selectionBackground":{background:"#222"},"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#d7d4f0"},"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#233"},".cm-cursorLayer":{pointerEvents:"none"},"&.cm-focused > .cm-scroller > .cm-cursorLayer":{animation:"steps(1) cm-blink 1.2s infinite"},// Two animations defined so that we can switch between them to
|
|
778
|
+
// restart the animation without forcing another style
|
|
779
|
+
// recomputation.
|
|
780
|
+
"@keyframes cm-blink":{"0%":{},"50%":{opacity:0},"100%":{}},"@keyframes cm-blink2":{"0%":{},"50%":{opacity:0},"100%":{}},".cm-cursor, .cm-dropCursor":{borderLeft:"1.2px solid black",marginLeft:"-0.6px",pointerEvents:"none"},".cm-cursor":{display:"none"},"&dark .cm-cursor":{borderLeftColor:"#ddd"},".cm-selectionHandle":{backgroundColor:"currentColor",width:"1.5px"},".cm-selectionHandle-start::before, .cm-selectionHandle-end::before":{content:"\"\"",backgroundColor:"inherit",borderRadius:"50%",width:"8px",height:"8px",position:"absolute",left:"-3.25px"},".cm-selectionHandle-start::before":{top:"-8px"},".cm-selectionHandle-end::before":{bottom:"-8px"},".cm-dropCursor":{position:"absolute"},"&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor":{display:"block"},".cm-iso":{unicodeBidi:"isolate"},".cm-announced":{position:"fixed",top:"-10000px"},"@media print":{".cm-announced":{display:"none"}},"&light .cm-activeLine":{backgroundColor:"#cceeff44"},"&dark .cm-activeLine":{backgroundColor:"#99eeff33"},"&light .cm-specialChar":{color:"red"},"&dark .cm-specialChar":{color:"#f78"},".cm-gutters":{flexShrink:0,display:"flex",height:"100%",boxSizing:"border-box",zIndex:200},".cm-gutters-before":{insetInlineStart:0},".cm-gutters-after":{insetInlineEnd:0},"&light .cm-gutters":{backgroundColor:"#f5f5f5",color:"#6c6c6c",border:"0px solid #ddd","&.cm-gutters-before":{borderRightWidth:"1px"},"&.cm-gutters-after":{borderLeftWidth:"1px"}},"&dark .cm-gutters":{backgroundColor:"#333338",color:"#ccc"},".cm-gutter":{display:"flex !important",// Necessary -- prevents margin collapsing
|
|
781
|
+
flexDirection:"column",flexShrink:0,boxSizing:"border-box",minHeight:"100%",overflow:"hidden"},".cm-gutterElement":{boxSizing:"border-box"},".cm-lineNumbers .cm-gutterElement":{padding:"0 3px 0 5px",minWidth:"20px",textAlign:"right",whiteSpace:"nowrap"},"&light .cm-activeLineGutter":{backgroundColor:"#e2f2ff"},"&dark .cm-activeLineGutter":{backgroundColor:"#222227"},".cm-panels":{boxSizing:"border-box",position:"sticky",left:0,right:0,zIndex:300},"&light .cm-panels":{backgroundColor:"#f5f5f5",color:"black"},"&light .cm-panels-top":{borderBottom:"1px solid #ddd"},"&light .cm-panels-bottom":{borderTop:"1px solid #ddd"},"&dark .cm-panels":{backgroundColor:"#333338",color:"white"},".cm-dialog":{padding:"2px 19px 4px 6px",position:"relative","& label":{fontSize:"80%"}},".cm-dialog-close":{position:"absolute",top:"3px",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",fontSize:"14px",padding:"0"},".cm-tab":{display:"inline-block",overflow:"hidden",verticalAlign:"bottom"},".cm-widgetBuffer":{verticalAlign:"text-top",height:"1em",width:0,display:"inline"},".cm-placeholder":{color:"#888",display:"inline-block",verticalAlign:"top",userSelect:"none"},".cm-highlightSpace":{backgroundImage:"radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)",backgroundPosition:"center"},".cm-highlightTab":{backgroundImage:`url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="20"><path stroke="%23888" stroke-width="1" fill="none" d="M1 10H196L190 5M190 15L196 10M197 4L197 16"/></svg>')`,backgroundSize:"auto 100%",backgroundPosition:"right 90%",backgroundRepeat:"no-repeat"},".cm-trailingSpace":{backgroundColor:"#ff332255"},".cm-button":{verticalAlign:"middle",color:"inherit",fontSize:"70%",padding:".2em 1em",borderRadius:"1px"},"&light .cm-button":{backgroundImage:"linear-gradient(#eff1f5, #d9d9df)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#b4b4b4, #d0d3d6)"}},"&dark .cm-button":{backgroundImage:"linear-gradient(#393939, #111)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#111, #333)"}},".cm-textfield":{verticalAlign:"middle",color:"inherit",fontSize:"70%",border:"1px solid silver",padding:".2em .5em"},"&light .cm-textfield":{backgroundColor:"white"},"&dark .cm-textfield":{border:"1px solid #555",backgroundColor:"inherit"}},lightDarkIDs),observeOptions={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},useCharData=browser.ie&&11>=browser.ie_version;// IE11 has very broken mutation observers, so we also listen to
|
|
782
|
+
// DOMCharacterDataModified there
|
|
783
|
+
class DOMObserver{constructor(view){this.view=view,this.active=!1,this.editContext=null,this.selectionRange=new DOMSelectionState,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resizeScroll=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.printQuery=null,this.parentCheck=-1,this.dom=view.contentDOM,this.observer=new MutationObserver(mutations=>{for(let mut of mutations)this.queue.push(mut);// IE11 will sometimes (on typing over a selection or
|
|
784
|
+
// backspacing out a single character text node) call the
|
|
785
|
+
// observer callback before actually updating the DOM.
|
|
786
|
+
//
|
|
787
|
+
// Unrelatedly, iOS Safari will, when ending a composition,
|
|
788
|
+
// sometimes first clear it, deliver the mutations, and then
|
|
789
|
+
// reinsert the finished text. CodeMirror's handling of the
|
|
790
|
+
// deletion will prevent the reinsertion from happening,
|
|
791
|
+
// breaking composition.
|
|
792
|
+
(browser.ie&&11>=browser.ie_version||browser.ios&&view.composing)&&mutations.some(m=>"childList"==m.type&&m.removedNodes.length||"characterData"==m.type&&m.oldValue.length>m.target.nodeValue.length)?this.flushSoon():this.flush()}),window.EditContext&&browser.android&&!1!==view.constructor.EDIT_CONTEXT&&// Chrome <126 doesn't support inverted selections in edit context (#1392)
|
|
793
|
+
!(browser.chrome&&126>browser.chrome_version)&&(this.editContext=new EditContextManager(view),view.state.facet(editable)&&(view.contentDOM.editContext=this.editContext.editContext)),useCharData&&(this.onCharData=event=>{this.queue.push({target:event.target,type:"characterData",oldValue:event.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),window.matchMedia&&(this.printQuery=window.matchMedia("print")),"function"==typeof ResizeObserver&&(this.resizeScroll=new ResizeObserver(()=>{var _a;(null===(_a=this.view.docView)||void 0===_a?void 0:_a.lastUpdate)<Date.now()-75&&this.onResize()}),this.resizeScroll.observe(view.scrollDOM)),this.addWindowListeners(this.win=view.win),this.start(),"function"==typeof IntersectionObserver&&(this.intersection=new IntersectionObserver(entries=>{0>this.parentCheck&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),0<entries.length&&0<entries[entries.length-1].intersectionRatio!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent("Event")))},{threshold:[0,.001]}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver(entries=>{0<entries.length&&0<entries[entries.length-1].intersectionRatio&&this.onScrollChanged(document.createEvent("Event"))},{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(e){this.view.inputState.runHandlers("scroll",e),this.intersecting&&this.view.measure()}onScroll(e){this.intersecting&&this.flush(!1),this.editContext&&this.view.requestMeasure(this.editContext.measureReq),this.onScrollChanged(e)}onResize(){0>this.resizeTimeout&&(this.resizeTimeout=setTimeout(()=>{this.resizeTimeout=-1,this.view.requestMeasure()},50))}onPrint(event){("change"!=event.type&&event.type||event.matches)&&(this.view.viewState.printing=!0,this.view.measure(),setTimeout(()=>{this.view.viewState.printing=!1,this.view.requestMeasure()},500))}updateGaps(gaps){if(this.gapIntersection&&(gaps.length!=this.gaps.length||this.gaps.some((g,i)=>g!=gaps[i]))){this.gapIntersection.disconnect();for(let gap of gaps)this.gapIntersection.observe(gap);this.gaps=gaps}}onSelectionChange(event){let wasChanged=this.selectionChanged;if(this.readSelectionRange()&&!this.delayedAndroidKey){let{view}=this,sel=this.selectionRange;if(view.state.facet(editable)?view.root.activeElement==this.dom:!!hasSelection(this.dom,sel)){let context=sel.anchorNode&&view.docView.tile.nearest(sel.anchorNode);// Deletions on IE11 fire their events in the wrong order, giving
|
|
794
|
+
// us a selection change event before the DOM changes are
|
|
795
|
+
// reported.
|
|
796
|
+
// Chrome Android has a similar issue when backspacing out a
|
|
797
|
+
// selection (#645).
|
|
798
|
+
return context&&context.isWidget()&&context.widget.ignoreEvent(event)?void(wasChanged||(this.selectionChanged=!1)):void((browser.ie&&11>=browser.ie_version||browser.android&&browser.chrome)&&!view.state.selection.main.empty&&// (Selection.isCollapsed isn't reliable on IE)
|
|
799
|
+
sel.focusNode&&isEquivalentPosition(sel.focusNode,sel.focusOffset,sel.anchorNode,sel.anchorOffset)?this.flushSoon():this.flush(!1))}}}readSelectionRange(){let{view}=this,selection=getSelection(view.root);// The Selection object is broken in shadow roots in Safari. See
|
|
800
|
+
// issue #414
|
|
801
|
+
if(!selection)return!1;let range=browser.safari&&11==view.root.nodeType&&view.root.activeElement==this.dom&&safariSelectionRangeHack(this.view,selection)||selection;if(!range||this.selectionRange.eq(range))return!1;let local=hasSelection(this.dom,range);// Detect the situation where the browser has, on focus, moved the
|
|
802
|
+
// selection to the start of the content element. Reset it to the
|
|
803
|
+
// position from the editor state.
|
|
804
|
+
return local&&!this.selectionChanged&&view.inputState.lastFocusTime>Date.now()-200&&view.inputState.lastTouchTime<Date.now()-300&&atElementStart(this.dom,range)?(this.view.inputState.lastFocusTime=0,view.docView.updateSelection(),!1):(this.selectionRange.setRange(range),local&&(this.selectionChanged=!0),!0)}setSelectionRange(anchor,head){this.selectionRange.set(anchor.node,anchor.offset,head.node,head.offset),this.selectionChanged=!1}clearSelectionRange(){this.selectionRange.set(null,0,null,0)}listenForScroll(){this.parentCheck=-1;let i=0,changed=null;for(let dom=this.dom;dom;)if(1==dom.nodeType)!changed&&i<this.scrollTargets.length&&this.scrollTargets[i]==dom?i++:!changed&&(changed=this.scrollTargets.slice(0,i)),changed&&changed.push(dom),dom=dom.assignedSlot||dom.parentNode;else if(11==dom.nodeType)// Shadow root
|
|
805
|
+
dom=dom.host;else break;if(i<this.scrollTargets.length&&!changed&&(changed=this.scrollTargets.slice(0,i)),changed){for(let dom of this.scrollTargets)dom.removeEventListener("scroll",this.onScroll);for(let dom of this.scrollTargets=changed)dom.addEventListener("scroll",this.onScroll)}}ignore(f){if(!this.active)return f();try{return this.stop(),f()}finally{this.start(),this.clear()}}start(){this.active||(this.observer.observe(this.dom,observeOptions),useCharData&&this.dom.addEventListener("DOMCharacterDataModified",this.onCharData),this.active=!0)}stop(){this.active&&(this.active=!1,this.observer.disconnect(),useCharData&&this.dom.removeEventListener("DOMCharacterDataModified",this.onCharData))}// Throw away any pending changes
|
|
806
|
+
clear(){this.processRecords(),this.queue.length=0,this.selectionChanged=!1}// Chrome Android, especially in combination with GBoard, not only
|
|
807
|
+
// doesn't reliably fire regular key events, but also often
|
|
808
|
+
// surrounds the effect of enter or backspace with a bunch of
|
|
809
|
+
// composition events that, when interrupted, cause text duplication
|
|
810
|
+
// or other kinds of corruption. This hack makes the editor back off
|
|
811
|
+
// from handling DOM changes for a moment when such a key is
|
|
812
|
+
// detected (via beforeinput or keydown), and then tries to flush
|
|
813
|
+
// them or, if that has no effect, dispatches the given key.
|
|
814
|
+
delayAndroidKey(key,keyCode){var _a;if(!this.delayedAndroidKey){let flush=()=>{let key=this.delayedAndroidKey;if(key){this.clearDelayedAndroidKey(),this.view.inputState.lastKeyCode=key.keyCode,this.view.inputState.lastKeyTime=Date.now();let flushed=this.flush();!flushed&&key.force&&dispatchKey(this.dom,key.key,key.keyCode)}};this.flushingAndroidKey=this.view.win.requestAnimationFrame(flush)}// Since backspace beforeinput is sometimes signalled spuriously,
|
|
815
|
+
// Enter always takes precedence.
|
|
816
|
+
this.delayedAndroidKey&&"Enter"!=key||(this.delayedAndroidKey={key,keyCode,// Only run the key handler when no changes are detected if
|
|
817
|
+
// this isn't coming right after another change, in which case
|
|
818
|
+
// it is probably part of a weird chain of updates, and should
|
|
819
|
+
// be ignored if it returns the DOM to its previous state.
|
|
820
|
+
force:this.lastChange<Date.now()-50||null!==(_a=this.delayedAndroidKey)&&void 0!==_a&&_a.force})}clearDelayedAndroidKey(){this.win.cancelAnimationFrame(this.flushingAndroidKey),this.delayedAndroidKey=null,this.flushingAndroidKey=-1}flushSoon(){0>this.delayedFlush&&(this.delayedFlush=this.view.win.requestAnimationFrame(()=>{this.delayedFlush=-1,this.flush()}))}forceFlush(){0<=this.delayedFlush&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}pendingRecords(){for(let mut of this.observer.takeRecords())this.queue.push(mut);return this.queue}processRecords(){let records=this.pendingRecords();records.length&&(this.queue=[]);let from=-1,to=-1,typeOver=!1;for(let record of records){let range=this.readMutation(record);range&&(range.typeOver&&(typeOver=!0),-1==from?({from,to}=range):(from=Math.min(range.from,from),to=Math.max(range.to,to)))}return{from,to,typeOver}}readChange(){let{from,to,typeOver}=this.processRecords(),newSel=this.selectionChanged&&hasSelection(this.dom,this.selectionRange);if(0>from&&!newSel)return null;-1<from&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1;let change=new DOMChange(this.view,from,to,typeOver);return this.view.docView.domChanged={newSel:change.newSel?change.newSel.main:null},change}// Apply pending changes, if any
|
|
821
|
+
flush(readSelection=!0){// Completely hold off flushing when pending keys are set—the code
|
|
822
|
+
// managing those will make sure processRecords is called and the
|
|
823
|
+
// view is resynchronized after
|
|
824
|
+
if(0<=this.delayedFlush||this.delayedAndroidKey)return!1;readSelection&&this.readSelectionRange();let domChange=this.readChange();if(!domChange)return this.view.requestMeasure(),!1;let startState=this.view.state,handled=applyDOMChange(this.view,domChange);// The view wasn't updated but DOM/selection changes were seen. Reset the view.
|
|
825
|
+
return this.view.state==startState&&(domChange.domChanged||domChange.newSel&&!sameSelPos(this.view.state.selection,domChange.newSel.main))&&this.view.update([]),handled}readMutation(rec){let tile=this.view.docView.tile.nearest(rec.target);if(!tile||tile.isWidget())return null;if(tile.markDirty("attributes"==rec.type),"childList"==rec.type){let childBefore=findChild(tile,rec.previousSibling||rec.target.previousSibling,-1),childAfter=findChild(tile,rec.nextSibling||rec.target.nextSibling,1);return{from:childBefore?tile.posAfter(childBefore):tile.posAtStart,to:childAfter?tile.posBefore(childAfter):tile.posAtEnd,typeOver:!1}}return"characterData"==rec.type?{from:tile.posAtStart,to:tile.posAtEnd,typeOver:rec.target.nodeValue==rec.oldValue}:null}setWindow(win){win!=this.win&&(this.removeWindowListeners(this.win),this.win=win,this.addWindowListeners(this.win))}addWindowListeners(win){win.addEventListener("resize",this.onResize),this.printQuery?this.printQuery.addEventListener?this.printQuery.addEventListener("change",this.onPrint):this.printQuery.addListener(this.onPrint):win.addEventListener("beforeprint",this.onPrint),win.addEventListener("scroll",this.onScroll),win.document.addEventListener("selectionchange",this.onSelectionChange)}removeWindowListeners(win){win.removeEventListener("scroll",this.onScroll),win.removeEventListener("resize",this.onResize),this.printQuery?this.printQuery.removeEventListener?this.printQuery.removeEventListener("change",this.onPrint):this.printQuery.removeListener(this.onPrint):win.removeEventListener("beforeprint",this.onPrint),win.document.removeEventListener("selectionchange",this.onSelectionChange)}update(update){this.editContext&&(this.editContext.update(update),update.startState.facet(editable)!=update.state.facet(editable)&&(update.view.contentDOM.editContext=update.state.facet(editable)?this.editContext.editContext:null))}destroy(){var _a,_b,_c;this.stop(),null===(_a=this.intersection)||void 0===_a?void 0:_a.disconnect(),null===(_b=this.gapIntersection)||void 0===_b?void 0:_b.disconnect(),null===(_c=this.resizeScroll)||void 0===_c?void 0:_c.disconnect();for(let dom of this.scrollTargets)dom.removeEventListener("scroll",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey),this.editContext&&(this.view.contentDOM.editContext=null,this.editContext.destroy())}}class EditContextManager{constructor(view){this.from=0,this.to=0,this.pendingContextChange=null,this.handlers=Object.create(null),this.composing=null,this.resetRange(view.state);let context=this.editContext=new window.EditContext({text:view.state.doc.sliceString(this.from,this.to),selectionStart:this.toContextPos(Math.max(this.from,Math.min(this.to,view.state.selection.main.anchor))),selectionEnd:this.toContextPos(view.state.selection.main.head)});for(let event in this.handlers.textupdate=e=>{let main=view.state.selection.main,{anchor,head}=main,from=this.toEditorPos(e.updateRangeStart),to=this.toEditorPos(e.updateRangeEnd);0<=view.inputState.composing&&!this.composing&&(this.composing={contextBase:e.updateRangeStart,editorBase:from,drifted:!1});let deletes=to-from>e.text.length;// If the window doesn't include the anchor, assume changes
|
|
826
|
+
// adjacent to a side go up to the anchor.
|
|
827
|
+
from==this.from&&anchor<this.from?from=anchor:to==this.to&&anchor>this.to&&(to=anchor);let diff=findDiff(view.state.sliceDoc(from,to),e.text,(deletes?main.from:main.to)-from,deletes?"end":null);// Edit contexts sometimes fire empty changes
|
|
828
|
+
if(!diff){let newSel=state.EditorSelection.single(this.toEditorPos(e.selectionStart),this.toEditorPos(e.selectionEnd));return void(sameSelPos(newSel,main)||view.dispatch({selection:newSel,userEvent:"select"}))}let change={from:diff.from+from,to:diff.toA+from,insert:state.Text.of(e.text.slice(diff.from,diff.toB).split("\n"))};if((browser.mac||browser.android)&&change.from==head-1&&/^\. ?$/.test(e.text)&&"off"==view.contentDOM.getAttribute("autocorrect")&&(change={from,to,insert:state.Text.of([e.text.replace("."," ")])}),this.pendingContextChange=change,!view.state.readOnly){let newLen=this.to-this.from+(change.to-change.from+change.insert.length);applyDOMChangeInner(view,change,state.EditorSelection.single(this.toEditorPos(e.selectionStart,newLen),this.toEditorPos(e.selectionEnd,newLen)))}// If the transaction didn't flush our change, revert it so
|
|
829
|
+
// that the context is in sync with the editor state again.
|
|
830
|
+
this.pendingContextChange&&(this.revertPending(view.state),this.setSelection(view.state)),change.from<change.to&&!change.insert.length&&0<=view.inputState.composing&&!/[\\p{Alphabetic}\\p{Number}_]/.test(context.text.slice(Math.max(0,e.updateRangeStart-1),Math.min(context.text.length,e.updateRangeStart+1)))&&this.handlers.compositionend(e)},this.handlers.characterboundsupdate=e=>{let rects=[],prev=null;for(let i=this.toEditorPos(e.rangeStart),end=this.toEditorPos(e.rangeEnd),rect;i<end;i++)rect=view.coordsForChar(i),prev=rect&&new DOMRect(rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top)||prev||new DOMRect,rects.push(prev);context.updateCharacterBounds(e.rangeStart,rects)},this.handlers.textformatupdate=e=>{let deco=[];for(let format of e.getTextFormats()){let lineStyle=format.underlineStyle,thickness=format.underlineThickness;if(!/none/i.test(lineStyle)&&!/none/i.test(thickness)){let from=this.toEditorPos(format.rangeStart),to=this.toEditorPos(format.rangeEnd);if(from<to){// These values changed from capitalized custom strings to lower-case CSS keywords in 2025
|
|
831
|
+
let style=`text-decoration: underline ${/^[a-z]/.test(lineStyle)?lineStyle+" ":"Dashed"==lineStyle?"dashed ":"Squiggle"==lineStyle?"wavy ":""}${/thin/i.test(thickness)?1:2}px`;deco.push(Decoration.mark({attributes:{style}}).range(from,to))}}}view.dispatch({effects:setEditContextFormatting.of(Decoration.set(deco))})},this.handlers.compositionstart=()=>{0>view.inputState.composing&&(view.inputState.composing=0,view.inputState.compositionFirstChange=!0)},this.handlers.compositionend=()=>{if(view.inputState.composing=-1,view.inputState.compositionFirstChange=null,this.composing){let{drifted}=this.composing;this.composing=null,drifted&&this.reset(view.state)}},this.handlers)context.addEventListener(event,this.handlers[event]);this.measureReq={read:view=>{this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect());let sel=getSelection(view.root);sel&&sel.rangeCount&&this.editContext.updateSelectionBounds(sel.getRangeAt(0).getBoundingClientRect())}}}applyEdits(update){let off=0,abort=!1,pending=this.pendingContextChange;return update.changes.iterChanges((fromA,toA,_fromB,_toB,insert)=>{if(!abort){let dLen=insert.length-(toA-fromA);if(pending&&toA>=pending.to){if(pending.from==fromA&&pending.to==toA&&pending.insert.eq(insert))return pending=this.pendingContextChange=null,off+=dLen,void(this.to+=dLen);pending=null,this.revertPending(update.state)}if(fromA+=off,toA+=off,toA<=this.from)this.from+=dLen,this.to+=dLen;else if(fromA<this.to){// Overlaps with window
|
|
832
|
+
if(fromA<this.from||toA>this.to||3e4<this.to-this.from+insert.length/* CxVp.MaxSize */)return void(abort=!0);this.editContext.updateText(this.toContextPos(fromA),this.toContextPos(toA),insert.toString()),this.to+=dLen}off+=dLen}}),pending&&!abort&&this.revertPending(update.state),!abort}update(update){let reverted=this.pendingContextChange,startSel=update.startState.selection.main;this.composing&&(this.composing.drifted||!update.changes.touchesRange(startSel.from,startSel.to)&&update.transactions.some(tr=>!tr.isUserEvent("input.type")&&tr.changes.touchesRange(this.from,this.to)))?(this.composing.drifted=!0,this.composing.editorBase=update.changes.mapPos(this.composing.editorBase)):this.applyEdits(update)&&this.rangeIsValid(update.state)?(update.docChanged||update.selectionSet||reverted)&&this.setSelection(update.state):(this.pendingContextChange=null,this.reset(update.state)),(update.geometryChanged||update.docChanged||update.selectionSet)&&update.view.requestMeasure(this.measureReq)}resetRange(state){let{head}=state.selection.main;this.from=Math.max(0,head-1e4/* CxVp.Margin */),this.to=Math.min(state.doc.length,head+1e4/* CxVp.Margin */)}reset(state){this.resetRange(state),this.editContext.updateText(0,this.editContext.text.length,state.doc.sliceString(this.from,this.to)),this.setSelection(state)}revertPending(state){let pending=this.pendingContextChange;this.pendingContextChange=null,this.editContext.updateText(this.toContextPos(pending.from),this.toContextPos(pending.from+pending.insert.length),state.doc.sliceString(pending.from,pending.to))}setSelection(state){let{main}=state.selection,start=this.toContextPos(Math.max(this.from,Math.min(this.to,main.anchor))),end=this.toContextPos(main.head);(this.editContext.selectionStart!=start||this.editContext.selectionEnd!=end)&&this.editContext.updateSelection(start,end)}rangeIsValid(state){let{head}=state.selection.main;return!(0<this.from&&500>head-this.from/* CxVp.MinMargin */||this.to<state.doc.length&&500>this.to-head/* CxVp.MinMargin */||30000<this.to-this.from)}toEditorPos(contextPos,clipLen=this.to-this.from){contextPos=Math.min(contextPos,clipLen);let c=this.composing;return c&&c.drifted?c.editorBase+(contextPos-c.contextBase):contextPos+this.from}toContextPos(editorPos){let c=this.composing;return c&&c.drifted?c.contextBase+(editorPos-c.editorBase):editorPos-this.from}destroy(){for(let event in this.handlers)this.editContext.removeEventListener(event,this.handlers[event])}}// The editor's update state machine looks something like this:
|
|
833
|
+
//
|
|
834
|
+
// Idle → Updating ⇆ Idle (unchecked) → Measuring → Idle
|
|
835
|
+
// ↑ ↓
|
|
836
|
+
// Updating (measure)
|
|
837
|
+
//
|
|
838
|
+
// The difference between 'Idle' and 'Idle (unchecked)' lies in
|
|
839
|
+
// whether a layout check has been scheduled. A regular update through
|
|
840
|
+
// the `update` method updates the DOM in a write-only fashion, and
|
|
841
|
+
// relies on a check (scheduled with `requestAnimationFrame`) to make
|
|
842
|
+
// sure everything is where it should be and the viewport covers the
|
|
843
|
+
// visible code. That check continues to measure and then optionally
|
|
844
|
+
// update until it reaches a coherent state.
|
|
845
|
+
/**
|
|
846
|
+
An editor view represents the editor's user interface. It holds
|
|
847
|
+
the editable DOM surface, and possibly other elements such as the
|
|
848
|
+
line number gutter. It handles events and dispatches state
|
|
849
|
+
transactions for editing actions.
|
|
850
|
+
*/class EditorView{/**
|
|
851
|
+
The current editor state.
|
|
852
|
+
*/get state(){return this.viewState.state}/**
|
|
853
|
+
To be able to display large documents without consuming too much
|
|
854
|
+
memory or overloading the browser, CodeMirror only draws the
|
|
855
|
+
code that is visible (plus a margin around it) to the DOM. This
|
|
856
|
+
property tells you the extent of the current drawn viewport, in
|
|
857
|
+
document positions.
|
|
858
|
+
*/get viewport(){return this.viewState.viewport}/**
|
|
859
|
+
When there are, for example, large collapsed ranges in the
|
|
860
|
+
viewport, its size can be a lot bigger than the actual visible
|
|
861
|
+
content. Thus, if you are doing something like styling the
|
|
862
|
+
content in the viewport, it is preferable to only do so for
|
|
863
|
+
these ranges, which are the subset of the viewport that is
|
|
864
|
+
actually drawn.
|
|
865
|
+
*/get visibleRanges(){return this.viewState.visibleRanges}/**
|
|
866
|
+
Returns false when the editor is entirely scrolled out of view
|
|
867
|
+
or otherwise hidden.
|
|
868
|
+
*/get inView(){return this.viewState.inView}/**
|
|
869
|
+
Indicates whether the user is currently composing text via
|
|
870
|
+
[IME](https://en.wikipedia.org/wiki/Input_method), and at least
|
|
871
|
+
one change has been made in the current composition.
|
|
872
|
+
*/get composing(){return!!this.inputState&&0<this.inputState.composing}/**
|
|
873
|
+
Indicates whether the user is currently in composing state. Note
|
|
874
|
+
that on some platforms, like Android, this will be the case a
|
|
875
|
+
lot, since just putting the cursor on a word starts a
|
|
876
|
+
composition there.
|
|
877
|
+
*/get compositionStarted(){return!!this.inputState&&0<=this.inputState.composing}/**
|
|
878
|
+
The document or shadow root that the view lives in.
|
|
879
|
+
*/get root(){return this._root}/**
|
|
880
|
+
@internal
|
|
881
|
+
*/get win(){return this.dom.ownerDocument.defaultView||window}/**
|
|
882
|
+
Construct a new view. You'll want to either provide a `parent`
|
|
883
|
+
option, or put `view.dom` into your document after creating a
|
|
884
|
+
view, so that the user can see the editor.
|
|
885
|
+
*/constructor(config={}){var _a;this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2/* UpdateState.Updating */,this.measureScheduled=-1,this.measureRequests=[],this.contentDOM=document.createElement("div"),this.scrollDOM=document.createElement("div"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className="cm-scroller",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement("div"),this.announceDOM.className="cm-announced",this.announceDOM.setAttribute("aria-live","polite"),this.dom=document.createElement("div"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),config.parent&&config.parent.appendChild(this.dom);let{dispatch}=config;this.dispatchTransactions=config.dispatchTransactions||dispatch&&(trs=>trs.forEach(tr=>dispatch(tr,this)))||(trs=>this.update(trs)),this.dispatch=this.dispatch.bind(this),this._root=config.root||getRoot(config.parent)||document,this.viewState=new ViewState(this,config.state||state.EditorState.create(config)),config.scrollTo&&config.scrollTo.is(scrollIntoView)&&(this.viewState.scrollTarget=config.scrollTo.value.clip(this.viewState.state)),this.plugins=this.state.facet(viewPlugin).map(spec=>new PluginInstance(spec));for(let plugin of this.plugins)plugin.update(this);this.observer=new DOMObserver(this),this.inputState=new InputState(this),this.inputState.ensureHandlers(this.plugins),this.docView=new DocView(this),this.mountStyles(),this.updateAttrs(),this.updateState=0/* UpdateState.Idle */,this.requestMeasure(),(null===(_a=document.fonts)||void 0===_a?void 0:_a.ready)&&document.fonts.ready.then(()=>{this.viewState.mustMeasureContent="refresh",this.requestMeasure()})}dispatch(...input){let trs=1==input.length&&input[0]instanceof state.Transaction?input:1==input.length&&Array.isArray(input[0])?input[0]:[this.state.update(...input)];this.dispatchTransactions(trs,this)}/**
|
|
886
|
+
Update the view for the given array of transactions. This will
|
|
887
|
+
update the visible document and selection to match the state
|
|
888
|
+
produced by the transactions, and notify view plugins of the
|
|
889
|
+
change. You should usually call
|
|
890
|
+
[`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead, which uses this
|
|
891
|
+
as a primitive.
|
|
892
|
+
*/update(transactions){if(0!=this.updateState/* UpdateState.Idle */)throw new Error("Calls to EditorView.update are not allowed while an update is in progress");let redrawn=!1,attrsChanged=!1,state$1=this.state,update;for(let tr of transactions){if(tr.startState!=state$1)throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");state$1=tr.state}if(this.destroyed)return void(this.viewState.state=state$1);let focus=this.hasFocus,focusFlag=0,dispatchFocus=null;transactions.some(tr=>tr.annotation(isFocusChange))?(this.inputState.notifiedFocused=focus,focusFlag=1/* UpdateFlag.Focus */):focus!=this.inputState.notifiedFocused&&(this.inputState.notifiedFocused=focus,dispatchFocus=focusChangeTransaction(state$1,focus),!dispatchFocus&&(focusFlag=1/* UpdateFlag.Focus */));// If there was a pending DOM change, eagerly read it and try to
|
|
893
|
+
// apply it after the given transactions.
|
|
894
|
+
let pendingKey=this.observer.delayedAndroidKey,domChange=null;// When the phrases change, redraw the editor
|
|
895
|
+
if(pendingKey?(this.observer.clearDelayedAndroidKey(),domChange=this.observer.readChange(),(domChange&&!this.state.doc.eq(state$1.doc)||!this.state.selection.eq(state$1.selection))&&(domChange=null)):this.observer.clear(),state$1.facet(state.EditorState.phrases)!=this.state.facet(state.EditorState.phrases))return this.setState(state$1);update=ViewUpdate.create(this,state$1,transactions),update.flags|=focusFlag;let scrollTarget=this.viewState.scrollTarget;try{this.updateState=2/* UpdateState.Updating */;for(let tr of transactions){if(scrollTarget&&(scrollTarget=scrollTarget.map(tr.changes)),tr.scrollIntoView){let{main}=tr.state.selection,{x,y}=this.state.facet(EditorView.cursorScrollMargin);scrollTarget=new ScrollTarget(main.empty?main:state.EditorSelection.cursor(main.head,main.head>main.anchor?-1:1),"nearest","nearest",y,x)}for(let e of tr.effects)e.is(scrollIntoView)&&(scrollTarget=e.value.clip(this.state))}this.viewState.update(update,scrollTarget),this.bidiCache=CachedOrder.update(this.bidiCache,update.changes),update.empty||(this.updatePlugins(update),this.inputState.update(update)),redrawn=this.docView.update(update),this.state.facet(styleModule)!=this.styleModules&&this.mountStyles(),attrsChanged=this.updateAttrs(),this.showAnnouncements(transactions),this.docView.updateSelection(redrawn,transactions.some(tr=>tr.isUserEvent("select.pointer")))}finally{this.updateState=0/* UpdateState.Idle */}if(update.startState.facet(theme)!=update.state.facet(theme)&&(this.viewState.mustMeasureContent=!0),(redrawn||attrsChanged||scrollTarget||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),redrawn&&this.docViewUpdate(),!update.empty)for(let listener of this.state.facet(updateListener))try{listener(update)}catch(e){logException(this.state,e,"update listener")}(dispatchFocus||domChange)&&Promise.resolve().then(()=>{dispatchFocus&&this.state==dispatchFocus.startState&&this.dispatch(dispatchFocus),domChange&&!applyDOMChange(this,domChange)&&pendingKey.force&&dispatchKey(this.contentDOM,pendingKey.key,pendingKey.keyCode)})}/**
|
|
896
|
+
Reset the view to the given state. (This will cause the entire
|
|
897
|
+
document to be redrawn and all view plugins to be reinitialized,
|
|
898
|
+
so you should probably only use it when the new state isn't
|
|
899
|
+
derived from the old state. Otherwise, use
|
|
900
|
+
[`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead.)
|
|
901
|
+
*/setState(newState){if(0!=this.updateState/* UpdateState.Idle */)throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");if(this.destroyed)return void(this.viewState.state=newState);this.updateState=2/* UpdateState.Updating */;let hadFocus=this.hasFocus;try{for(let plugin of this.plugins)plugin.destroy(this);this.viewState=new ViewState(this,newState),this.plugins=newState.facet(viewPlugin).map(spec=>new PluginInstance(spec)),this.pluginMap.clear();for(let plugin of this.plugins)plugin.update(this);this.docView.destroy(),this.docView=new DocView(this),this.inputState.ensureHandlers(this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0/* UpdateState.Idle */}hadFocus&&this.focus(),this.requestMeasure()}updatePlugins(update){let prevSpecs=update.startState.facet(viewPlugin),specs=update.state.facet(viewPlugin);if(prevSpecs!=specs){let newPlugins=[];for(let spec of specs){let found=prevSpecs.indexOf(spec);if(0>found)newPlugins.push(new PluginInstance(spec));else{let plugin=this.plugins[found];plugin.mustUpdate=update,newPlugins.push(plugin)}}for(let plugin of this.plugins)plugin.mustUpdate!=update&&plugin.destroy(this);this.plugins=newPlugins,this.pluginMap.clear()}else for(let p of this.plugins)p.mustUpdate=update;for(let i=0;i<this.plugins.length;i++)this.plugins[i].update(this);prevSpecs!=specs&&this.inputState.ensureHandlers(this.plugins)}docViewUpdate(){for(let plugin of this.plugins){let val=plugin.value;if(val&&val.docViewUpdate)try{val.docViewUpdate(this)}catch(e){logException(this.state,e,"doc view update listener")}}}/**
|
|
902
|
+
@internal
|
|
903
|
+
*/measure(flush=!0){if(this.destroyed)return;if(-1<this.measureScheduled&&this.win.cancelAnimationFrame(this.measureScheduled),this.observer.delayedAndroidKey)return this.measureScheduled=-1,void this.requestMeasure();this.measureScheduled=0,flush&&this.observer.forceFlush();let updated=null,scroll=this.viewState.scrollParent,scrollOffset=this.viewState.getScrollOffset(),{scrollAnchorPos,scrollAnchorHeight}=this.viewState;1<Math.abs(scrollOffset-this.viewState.scrollOffset)&&(scrollAnchorHeight=-1),this.viewState.scrollAnchorHeight=-1;try{for(let i=0;;i++){if(0>scrollAnchorHeight)if(isScrolledToBottom(scroll||this.win))scrollAnchorPos=-1,scrollAnchorHeight=this.viewState.heightMap.height;else{let block=this.viewState.scrollAnchorAt(scrollOffset);scrollAnchorPos=block.from,scrollAnchorHeight=block.top}this.updateState=1/* UpdateState.Measuring */;let changed=this.viewState.measure();if(!changed&&!this.measureRequests.length&&null==this.viewState.scrollTarget)break;if(5<i){console.warn(this.measureRequests.length?"Measure loop restarted more than 5 times":"Viewport failed to stabilize");break}let measuring=[];// Only run measure requests in this cycle when the viewport didn't change
|
|
904
|
+
4&changed/* UpdateFlag.Viewport */||([this.measureRequests,measuring]=[measuring,this.measureRequests]);let measured=measuring.map(m=>{try{return m.read(this)}catch(e){return logException(this.state,e),BadMeasure}}),update=ViewUpdate.create(this,this.state,[]),redrawn=!1;update.flags|=changed,updated?updated.flags|=changed:updated=update,this.updateState=2/* UpdateState.Updating */,update.empty||(this.updatePlugins(update),this.inputState.update(update),this.updateAttrs(),redrawn=this.docView.update(update),redrawn&&this.docViewUpdate());for(let i=0;i<measuring.length;i++)if(measured[i]!=BadMeasure)try{let m=measuring[i];m.write&&m.write(measured[i],this)}catch(e){logException(this.state,e)}if(redrawn&&this.docView.updateSelection(!0),!update.viewportChanged&&0==this.measureRequests.length){if(this.viewState.editorHeight)if(this.viewState.scrollTarget){this.docView.scrollIntoView(this.viewState.scrollTarget),this.viewState.scrollTarget=null,scrollAnchorHeight=-1;continue}else{let newAnchorHeight=0>scrollAnchorPos?this.viewState.heightMap.height:this.viewState.lineBlockAt(scrollAnchorPos).top,diff=(newAnchorHeight-scrollAnchorHeight)/this.scaleY;if((1<diff||-1>diff)&&(scroll==this.scrollDOM||this.hasFocus||Math.max(this.inputState.lastWheelEvent,this.inputState.lastTouchTime)>Date.now()-100)){scrollOffset+=diff,scroll?scroll.scrollTop+=diff:this.win.scrollBy(0,diff),scrollAnchorHeight=-1;continue}}break}}}finally{this.updateState=0/* UpdateState.Idle */,this.measureScheduled=-1}if(updated&&!updated.empty)for(let listener of this.state.facet(updateListener))listener(updated)}/**
|
|
905
|
+
Get the CSS classes for the currently active editor themes.
|
|
906
|
+
*/get themeClasses(){return baseThemeID+" "+(this.state.facet(darkTheme)?baseDarkID:baseLightID)+" "+this.state.facet(theme)}updateAttrs(){let editorAttrs=attrsFromFacet(this,editorAttributes,{class:"cm-editor"+(this.hasFocus?" cm-focused ":" ")+this.themeClasses}),contentAttrs={spellcheck:"false",autocorrect:"off",autocapitalize:"off",writingsuggestions:"false",translate:"no",contenteditable:this.state.facet(editable)?"true":"false",class:"cm-content",style:`${browser.tabSize}: ${this.state.tabSize}`,role:"textbox","aria-multiline":"true"};this.state.readOnly&&(contentAttrs["aria-readonly"]="true"),attrsFromFacet(this,contentAttributes,contentAttrs);let changed=this.observer.ignore(()=>{let changedContent=updateAttrs(this.contentDOM,this.contentAttrs,contentAttrs),changedEditor=updateAttrs(this.dom,this.editorAttrs,editorAttrs);return changedContent||changedEditor});return this.editorAttrs=editorAttrs,this.contentAttrs=contentAttrs,changed}showAnnouncements(trs){let first=!0;for(let tr of trs)for(let effect of tr.effects)if(effect.is(EditorView.announce)){first&&(this.announceDOM.textContent=""),first=!1;let div=this.announceDOM.appendChild(document.createElement("div"));div.textContent=effect.value}}mountStyles(){this.styleModules=this.state.facet(styleModule);let nonce=this.state.facet(EditorView.cspNonce);styleMod.StyleModule.mount(this.root,this.styleModules.concat(baseTheme$1).reverse(),nonce?{nonce}:void 0)}readMeasured(){if(2==this.updateState/* UpdateState.Updating */)throw new Error("Reading the editor layout isn't allowed during an update");0==this.updateState/* UpdateState.Idle */&&-1<this.measureScheduled&&this.measure(!1)}/**
|
|
907
|
+
Schedule a layout measurement, optionally providing callbacks to
|
|
908
|
+
do custom DOM measuring followed by a DOM write phase. Using
|
|
909
|
+
this is preferable reading DOM layout directly from, for
|
|
910
|
+
example, an event handler, because it'll make sure measuring and
|
|
911
|
+
drawing done by other components is synchronized, avoiding
|
|
912
|
+
unnecessary DOM layout computations.
|
|
913
|
+
*/requestMeasure(request){if(0>this.measureScheduled&&(this.measureScheduled=this.win.requestAnimationFrame(()=>this.measure())),request){if(-1<this.measureRequests.indexOf(request))return;if(null!=request.key)for(let i=0;i<this.measureRequests.length;i++)if(this.measureRequests[i].key===request.key)return void(this.measureRequests[i]=request);this.measureRequests.push(request)}}/**
|
|
914
|
+
Get the value of a specific plugin, if present. Note that
|
|
915
|
+
plugins that crash can be dropped from a view, so even when you
|
|
916
|
+
know you registered a given plugin, it is recommended to check
|
|
917
|
+
the return value of this method.
|
|
918
|
+
*/plugin(plugin){let known=this.pluginMap.get(plugin);return(void 0===known||known&&known.plugin!=plugin)&&this.pluginMap.set(plugin,known=this.plugins.find(p=>p.plugin==plugin)||null),known&&known.update(this).value}/**
|
|
919
|
+
The top position of the document, in screen coordinates. This
|
|
920
|
+
may be negative when the editor is scrolled down. Points
|
|
921
|
+
directly to the top of the first line, not above the padding.
|
|
922
|
+
*/get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}/**
|
|
923
|
+
Reports the padding above and below the document.
|
|
924
|
+
*/get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}/**
|
|
925
|
+
If the editor is transformed with CSS, this provides the scale
|
|
926
|
+
along the X axis. Otherwise, it will just be 1. Note that
|
|
927
|
+
transforms other than translation and scaling are not supported.
|
|
928
|
+
*/get scaleX(){return this.viewState.scaleX}/**
|
|
929
|
+
Provide the CSS transformed scale along the Y axis.
|
|
930
|
+
*/get scaleY(){return this.viewState.scaleY}/**
|
|
931
|
+
Find the text line or block widget at the given vertical
|
|
932
|
+
position (which is interpreted as relative to the [top of the
|
|
933
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
|
|
934
|
+
*/elementAtHeight(height){return this.readMeasured(),this.viewState.elementAtHeight(height)}/**
|
|
935
|
+
Find the line block (see
|
|
936
|
+
[`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt)) at the given
|
|
937
|
+
height, again interpreted relative to the [top of the
|
|
938
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop).
|
|
939
|
+
*/lineBlockAtHeight(height){return this.readMeasured(),this.viewState.lineBlockAtHeight(height)}/**
|
|
940
|
+
Get the extent and vertical position of all [line
|
|
941
|
+
blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
|
|
942
|
+
are relative to the [top of the
|
|
943
|
+
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
|
|
944
|
+
*/get viewportLineBlocks(){return this.viewState.viewportLines}/**
|
|
945
|
+
Find the line block around the given document position. A line
|
|
946
|
+
block is a range delimited on both sides by either a
|
|
947
|
+
non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line break, or the
|
|
948
|
+
start/end of the document. It will usually just hold a line of
|
|
949
|
+
text, but may be broken into multiple textblocks by block
|
|
950
|
+
widgets.
|
|
951
|
+
*/lineBlockAt(pos){return this.viewState.lineBlockAt(pos)}/**
|
|
952
|
+
The editor's total content height.
|
|
953
|
+
*/get contentHeight(){return this.viewState.contentHeight}/**
|
|
954
|
+
Move a cursor position by [grapheme
|
|
955
|
+
cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether
|
|
956
|
+
the motion is away from the line start, or towards it. In
|
|
957
|
+
bidirectional text, the line is traversed in visual order, using
|
|
958
|
+
the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
959
|
+
When the start position was the last one on the line, the
|
|
960
|
+
returned position will be across the line break. If there is no
|
|
961
|
+
further line, the original position is returned.
|
|
962
|
+
|
|
963
|
+
By default, this method moves over a single cluster. The
|
|
964
|
+
optional `by` argument can be used to move across more. It will
|
|
965
|
+
be called with the first cluster as argument, and should return
|
|
966
|
+
a predicate that determines, for each subsequent cluster,
|
|
967
|
+
whether it should also be moved over.
|
|
968
|
+
*/moveByChar(start,forward,by){return skipAtoms(this,start,moveByChar(this,start,forward,by))}/**
|
|
969
|
+
Move a cursor position across the next group of either
|
|
970
|
+
[letters](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) or non-letter
|
|
971
|
+
non-whitespace characters.
|
|
972
|
+
*/moveByGroup(start,forward){return skipAtoms(this,start,moveByChar(this,start,forward,initial=>byGroup(this,start.head,initial)))}/**
|
|
973
|
+
Get the cursor position visually at the start or end of a line.
|
|
974
|
+
Note that this may differ from the _logical_ position at its
|
|
975
|
+
start or end (which is simply at `line.from`/`line.to`) if text
|
|
976
|
+
at the start or end goes against the line's base text direction.
|
|
977
|
+
*/visualLineSide(line,end){let order=this.bidiSpans(line),dir=this.textDirectionAt(line.from),span=order[end?order.length-1:0];return state.EditorSelection.cursor(span.side(end,dir)+line.from,span.forward(!end,dir)?1:-1)}/**
|
|
978
|
+
Move to the next line boundary in the given direction. If
|
|
979
|
+
`includeWrap` is true, line wrapping is on, and there is a
|
|
980
|
+
further wrap point on the current line, the wrap point will be
|
|
981
|
+
returned. Otherwise this function will return the start or end
|
|
982
|
+
of the line.
|
|
983
|
+
*/moveToLineBoundary(start,forward,includeWrap=!0){return moveToLineBoundary(this,start,forward,includeWrap)}/**
|
|
984
|
+
Move a cursor position vertically. When `distance` isn't given,
|
|
985
|
+
it defaults to moving to the next line (including wrapped
|
|
986
|
+
lines). Otherwise, `distance` should provide a positive distance
|
|
987
|
+
in pixels.
|
|
988
|
+
|
|
989
|
+
When `start` has a
|
|
990
|
+
[`goalColumn`](https://codemirror.net/6/docs/ref/#state.SelectionRange.goalColumn), the vertical
|
|
991
|
+
motion will use that as a target horizontal position. Otherwise,
|
|
992
|
+
the cursor's own horizontal position is used. The returned
|
|
993
|
+
cursor will have its goal column set to whichever column was
|
|
994
|
+
used.
|
|
995
|
+
*/moveVertically(start,forward,distance){return skipAtoms(this,start,moveVertically(this,start,forward,distance))}/**
|
|
996
|
+
Find the DOM parent node and offset (child offset if `node` is
|
|
997
|
+
an element, character offset when it is a text node) at the
|
|
998
|
+
given document position.
|
|
999
|
+
|
|
1000
|
+
Note that for positions that aren't currently in
|
|
1001
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
1002
|
+
meaningful (it may just point before or after a placeholder
|
|
1003
|
+
element).
|
|
1004
|
+
*/domAtPos(pos,side=1){return this.docView.domAtPos(pos,side)}/**
|
|
1005
|
+
Find the document position at the given DOM node. Can be useful
|
|
1006
|
+
for associating positions with DOM events. Will raise an error
|
|
1007
|
+
when `node` isn't part of the editor content.
|
|
1008
|
+
*/posAtDOM(node,offset=0){return this.docView.posFromDOM(node,offset)}posAtCoords(coords,precise=!0){this.readMeasured();let found=posAtCoords(this,coords,precise);return found&&found.pos}posAndSideAtCoords(coords,precise=!0){return this.readMeasured(),posAtCoords(this,coords,precise)}/**
|
|
1009
|
+
Get the screen coordinates at the given document position.
|
|
1010
|
+
`side` determines whether the coordinates are based on the
|
|
1011
|
+
element before (-1) or after (1) the position (if no element is
|
|
1012
|
+
available on the given side, the method will transparently use
|
|
1013
|
+
another strategy to get reasonable coordinates).
|
|
1014
|
+
*/coordsAtPos(pos,side=1){this.readMeasured();let rect=this.docView.coordsAt(pos,side);if(!rect||rect.left==rect.right)return rect;let line=this.state.doc.lineAt(pos),order=this.bidiSpans(line),span=order[BidiSpan.find(order,pos-line.from,-1,side)];return flattenRect(rect,span.dir==exports.Direction.LTR==0<side)}/**
|
|
1015
|
+
Return the rectangle around a given character. If `pos` does not
|
|
1016
|
+
point in front of a character that is in the viewport and
|
|
1017
|
+
rendered (i.e. not replaced, not a line break), this will return
|
|
1018
|
+
null. For space characters that are a line wrap point, this will
|
|
1019
|
+
return the position before the line break.
|
|
1020
|
+
*/coordsForChar(pos){return this.readMeasured(),this.docView.coordsForChar(pos)}/**
|
|
1021
|
+
The default width of a character in the editor. May not
|
|
1022
|
+
accurately reflect the width of all characters (given variable
|
|
1023
|
+
width fonts or styling of invididual ranges).
|
|
1024
|
+
*/get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}/**
|
|
1025
|
+
The default height of a line in the editor. May not be accurate
|
|
1026
|
+
for all lines.
|
|
1027
|
+
*/get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}/**
|
|
1028
|
+
The text direction
|
|
1029
|
+
([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
|
|
1030
|
+
CSS property) of the editor's content element.
|
|
1031
|
+
*/get textDirection(){return this.viewState.defaultTextDirection}/**
|
|
1032
|
+
Find the text direction of the block at the given position, as
|
|
1033
|
+
assigned by CSS. If
|
|
1034
|
+
[`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection)
|
|
1035
|
+
isn't enabled, or the given position is outside of the viewport,
|
|
1036
|
+
this will always return the same as
|
|
1037
|
+
[`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that
|
|
1038
|
+
this may trigger a DOM layout.
|
|
1039
|
+
*/textDirectionAt(pos){let perLine=this.state.facet(perLineTextDirection);return!perLine||pos<this.viewport.from||pos>this.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(pos))}/**
|
|
1040
|
+
Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
|
|
1041
|
+
(as determined by the
|
|
1042
|
+
[`white-space`](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space)
|
|
1043
|
+
CSS property of its content element).
|
|
1044
|
+
*/get lineWrapping(){return this.viewState.heightOracle.lineWrapping}/**
|
|
1045
|
+
Returns the bidirectional text structure of the given line
|
|
1046
|
+
(which should be in the current document) as an array of span
|
|
1047
|
+
objects. The order of these spans matches the [text
|
|
1048
|
+
direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection)—if that is
|
|
1049
|
+
left-to-right, the leftmost spans come first, otherwise the
|
|
1050
|
+
rightmost spans come first.
|
|
1051
|
+
*/bidiSpans(line){if(line.length>MaxBidiLine)return trivialOrder(line.length);let dir=this.textDirectionAt(line.from),isolates;for(let entry of this.bidiCache)if(entry.from==line.from&&entry.dir==dir&&(entry.fresh||isolatesEq(entry.isolates,isolates=getIsolatedRanges(this,line))))return entry.order;isolates||(isolates=getIsolatedRanges(this,line));let order=computeOrder(line.text,dir,isolates);return this.bidiCache.push(new CachedOrder(line.from,line.to,dir,isolates,!0,order)),order}/**
|
|
1052
|
+
Check whether the editor has focus.
|
|
1053
|
+
*/get hasFocus(){var _a;// Safari return false for hasFocus when the context menu is open
|
|
1054
|
+
// or closing, which leads us to ignore selection changes from the
|
|
1055
|
+
// context menu because it looks like the editor isn't focused.
|
|
1056
|
+
// This kludges around that.
|
|
1057
|
+
return(this.dom.ownerDocument.hasFocus()||browser.safari&&(null===(_a=this.inputState)||void 0===_a?void 0:_a.lastContextMenu)>Date.now()-3e4)&&this.root.activeElement==this.contentDOM}/**
|
|
1058
|
+
Put focus on the editor.
|
|
1059
|
+
*/focus(){this.observer.ignore(()=>{focusPreventScroll(this.contentDOM),this.docView.updateSelection()})}/**
|
|
1060
|
+
Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only
|
|
1061
|
+
necessary when moving the editor's existing DOM to a new window or shadow root.
|
|
1062
|
+
*/setRoot(root){this._root!=root&&(this._root=root,this.observer.setWindow((9==root.nodeType?root:root.ownerDocument).defaultView||window),this.mountStyles())}/**
|
|
1063
|
+
Clean up this editor view, removing its element from the
|
|
1064
|
+
document, unregistering event handlers, and notifying
|
|
1065
|
+
plugins. The view instance can no longer be used after
|
|
1066
|
+
calling this.
|
|
1067
|
+
*/destroy(){this.root.activeElement==this.contentDOM&&this.contentDOM.blur();for(let plugin of this.plugins)plugin.destroy(this);this.plugins=[],this.inputState.destroy(),this.docView.destroy(),this.dom.remove(),this.observer.destroy(),-1<this.measureScheduled&&this.win.cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}/**
|
|
1068
|
+
Returns an effect that can be
|
|
1069
|
+
[added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
|
|
1070
|
+
cause it to scroll the given position or range into view.
|
|
1071
|
+
*/static scrollIntoView(pos,options={}){var _a,_b,_c,_d;return scrollIntoView.of(new ScrollTarget("number"==typeof pos?state.EditorSelection.cursor(pos):pos,null!==(_a=options.y)&&void 0!==_a?_a:"nearest",null!==(_b=options.x)&&void 0!==_b?_b:"nearest",null!==(_c=options.yMargin)&&void 0!==_c?_c:5,null!==(_d=options.xMargin)&&void 0!==_d?_d:5))}/**
|
|
1072
|
+
Return an effect that resets the editor to its current (at the
|
|
1073
|
+
time this method was called) scroll position. Note that this
|
|
1074
|
+
only affects the editor's own scrollable element, not parents.
|
|
1075
|
+
See also
|
|
1076
|
+
[`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
|
|
1077
|
+
|
|
1078
|
+
The effect should be used with a document identical to the one
|
|
1079
|
+
it was created for. Failing to do so is not an error, but may
|
|
1080
|
+
not scroll to the expected position. You can
|
|
1081
|
+
[map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
|
|
1082
|
+
*/scrollSnapshot(){let{scrollTop,scrollLeft}=this.scrollDOM,ref=this.viewState.scrollAnchorAt(scrollTop);return scrollIntoView.of(new ScrollTarget(state.EditorSelection.cursor(ref.from),"start","start",ref.top-scrollTop,scrollLeft,!0))}/**
|
|
1083
|
+
Enable or disable tab-focus mode, which disables key bindings
|
|
1084
|
+
for Tab and Shift-Tab, letting the browser's default
|
|
1085
|
+
focus-changing behavior go through instead. This is useful to
|
|
1086
|
+
prevent trapping keyboard users in your editor.
|
|
1087
|
+
|
|
1088
|
+
Without argument, this toggles the mode. With a boolean, it
|
|
1089
|
+
enables (true) or disables it (false). Given a number, it
|
|
1090
|
+
temporarily enables the mode until that number of milliseconds
|
|
1091
|
+
have passed or another non-Tab key is pressed.
|
|
1092
|
+
*/setTabFocusMode(to){null==to?this.inputState.tabFocusMode=0>this.inputState.tabFocusMode?0:-1:"boolean"==typeof to?this.inputState.tabFocusMode=to?0:-1:0!=this.inputState.tabFocusMode&&(this.inputState.tabFocusMode=Date.now()+to)}/**
|
|
1093
|
+
Returns an extension that can be used to add DOM event handlers.
|
|
1094
|
+
The value should be an object mapping event names to handler
|
|
1095
|
+
functions. For any given event, such functions are ordered by
|
|
1096
|
+
extension precedence, and the first handler to return true will
|
|
1097
|
+
be assumed to have handled that event, and no other handlers or
|
|
1098
|
+
built-in behavior will be activated for it. These are registered
|
|
1099
|
+
on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
|
|
1100
|
+
for `scroll` handlers, which will be called any time the
|
|
1101
|
+
editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
|
|
1102
|
+
its parent nodes is scrolled.
|
|
1103
|
+
*/static domEventHandlers(handlers){return ViewPlugin.define(()=>({}),{eventHandlers:handlers})}/**
|
|
1104
|
+
Create an extension that registers DOM event observers. Contrary
|
|
1105
|
+
to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
|
|
1106
|
+
observers can't be prevented from running by a higher-precedence
|
|
1107
|
+
handler returning true. They also don't prevent other handlers
|
|
1108
|
+
and observers from running when they return true, and should not
|
|
1109
|
+
call `preventDefault`.
|
|
1110
|
+
*/static domEventObservers(observers){return ViewPlugin.define(()=>({}),{eventObservers:observers})}/**
|
|
1111
|
+
Create a theme extension. The first argument can be a
|
|
1112
|
+
[`style-mod`](https://code.haverbeke.berlin/marijn/style-mod#documentation)
|
|
1113
|
+
style spec providing the styles for the theme. These will be
|
|
1114
|
+
prefixed with a generated class for the style.
|
|
1115
|
+
|
|
1116
|
+
Because the selectors will be prefixed with a scope class, rule
|
|
1117
|
+
that directly match the editor's [wrapper
|
|
1118
|
+
element](https://codemirror.net/6/docs/ref/#view.EditorView.dom)—to which the scope class will be
|
|
1119
|
+
added—need to be explicitly differentiated by adding an `&` to
|
|
1120
|
+
the selector for that element—for example
|
|
1121
|
+
`&.cm-focused`.
|
|
1122
|
+
|
|
1123
|
+
When `dark` is set to true, the theme will be marked as dark,
|
|
1124
|
+
which will cause the `&dark` rules from [base
|
|
1125
|
+
themes](https://codemirror.net/6/docs/ref/#view.EditorView^baseTheme) to be used (as opposed to
|
|
1126
|
+
`&light` when a light theme is active).
|
|
1127
|
+
*/static theme(spec,options){let prefix=styleMod.StyleModule.newName(),result=[theme.of(prefix),styleModule.of(buildTheme(`.${prefix}`,spec))];return options&&options.dark&&result.push(darkTheme.of(!0)),result}/**
|
|
1128
|
+
Create an extension that adds styles to the base theme. Like
|
|
1129
|
+
with [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme), use `&` to indicate the
|
|
1130
|
+
place of the editor wrapper element when directly targeting
|
|
1131
|
+
that. You can also use `&dark` or `&light` instead to only
|
|
1132
|
+
target editors with a dark or light theme.
|
|
1133
|
+
*/static baseTheme(spec){return state.Prec.lowest(styleModule.of(buildTheme("."+baseThemeID,spec,lightDarkIDs)))}/**
|
|
1134
|
+
Retrieve an editor view instance from the view's DOM
|
|
1135
|
+
representation.
|
|
1136
|
+
*/static findFromDOM(dom){var _a;let content=dom.querySelector(".cm-content"),tile=content&&Tile.get(content)||Tile.get(dom);return(null===(_a=null===tile||void 0===tile?void 0:tile.root)||void 0===_a?void 0:_a.view)||null}}/**
|
|
1137
|
+
Facet to add a [style
|
|
1138
|
+
module](https://code.haverbeke.berlin/marijn/style-mod#documentation) to
|
|
1139
|
+
an editor view. The view will ensure that the module is
|
|
1140
|
+
mounted in its [document
|
|
1141
|
+
root](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.root).
|
|
1142
|
+
*/EditorView.styleModule=styleModule,EditorView.inputHandler=inputHandler,EditorView.clipboardInputFilter=clipboardInputFilter,EditorView.clipboardOutputFilter=clipboardOutputFilter,EditorView.scrollHandler=scrollHandler,EditorView.focusChangeEffect=focusChangeEffect,EditorView.perLineTextDirection=perLineTextDirection,EditorView.exceptionSink=exceptionSink,EditorView.updateListener=updateListener,EditorView.editable=editable,EditorView.mouseSelectionStyle=mouseSelectionStyle,EditorView.dragMovesSelection=dragMovesSelection$1,EditorView.clickAddsSelectionRange=clickAddsSelectionRange,EditorView.decorations=decorations,EditorView.blockWrappers=blockWrappers,EditorView.outerDecorations=outerDecorations,EditorView.atomicRanges=atomicRanges,EditorView.bidiIsolatedRanges=bidiIsolatedRanges,EditorView.cursorScrollMargin=state.Facet.define({combine:inputs=>{let x=5,y=5;for(let i of inputs)"number"==typeof i?x=y=i:({x,y}=i);return{x,y}}}),EditorView.scrollMargins=scrollMargins,EditorView.darkTheme=darkTheme,EditorView.cspNonce=state.Facet.define({combine:values=>values.length?values[0]:""}),EditorView.contentAttributes=contentAttributes,EditorView.editorAttributes=editorAttributes,EditorView.lineWrapping=EditorView.contentAttributes.of({class:"cm-lineWrapping"}),EditorView.announce=state.StateEffect.define();// Maximum line length for which we compute accurate bidi info
|
|
1143
|
+
const MaxBidiLine=4096,BadMeasure={};class CachedOrder{constructor(from,to,dir,isolates,fresh,order){this.from=from,this.to=to,this.dir=dir,this.isolates=isolates,this.fresh=fresh,this.order=order}static update(cache,changes){if(changes.empty&&!cache.some(c=>c.fresh))return cache;let result=[],lastDir=cache.length?cache[cache.length-1].dir:exports.Direction.LTR;for(let i=Math.max(0,cache.length-10),entry;i<cache.length;i++)entry=cache[i],entry.dir!=lastDir||changes.touchesRange(entry.from,entry.to)||result.push(new CachedOrder(changes.mapPos(entry.from,1),changes.mapPos(entry.to,-1),entry.dir,entry.isolates,!1,entry.order));return result}}const currentPlatform=browser.mac?"mac":browser.windows?"win":browser.linux?"linux":"key",handleKeyEvents=state.Prec.default(EditorView.domEventHandlers({keydown(event,view){return runHandlers(getKeymap(view.state),event,view,"editor")}})),keymap=state.Facet.define({enables:handleKeyEvents}),Keymaps=new WeakMap;/**
|
|
1144
|
+
Facet used for registering keymaps.
|
|
1145
|
+
|
|
1146
|
+
You can add multiple keymaps to an editor. Their priorities
|
|
1147
|
+
determine their precedence (the ones specified early or with high
|
|
1148
|
+
priority get checked first). When a handler has returned `true`
|
|
1149
|
+
for a given key, no further handlers are called.
|
|
1150
|
+
*/let storedPrefix=null;const PrefixTimeout=4e3;let currentKeyEvent=null;class RectangleMarker{/**
|
|
1151
|
+
Create a marker with the given class and dimensions. If `width`
|
|
1152
|
+
is null, the DOM element will get no width style.
|
|
1153
|
+
*/constructor(className,/**
|
|
1154
|
+
The left position of the marker (in pixels, document-relative).
|
|
1155
|
+
*/left,/**
|
|
1156
|
+
The top position of the marker.
|
|
1157
|
+
*/top,/**
|
|
1158
|
+
The width of the marker, or null if it shouldn't get a width assigned.
|
|
1159
|
+
*/width,/**
|
|
1160
|
+
The height of the marker.
|
|
1161
|
+
*/height){this.className=className,this.left=left,this.top=top,this.width=width,this.height=height}draw(){let elt=document.createElement("div");return elt.className=this.className,this.adjust(elt),elt}update(elt,prev){return prev.className==this.className&&(this.adjust(elt),!0)}adjust(elt){elt.style.left=this.left+"px",elt.style.top=this.top+"px",null!=this.width&&(elt.style.width=this.width+"px"),elt.style.height=this.height+"px"}eq(p){return this.left==p.left&&this.top==p.top&&this.width==p.width&&this.height==p.height&&this.className==p.className}/**
|
|
1162
|
+
Create a set of rectangles for the given selection range,
|
|
1163
|
+
assigning them theclass`className`. Will create a single
|
|
1164
|
+
rectangle for empty ranges, and a set of selection-style
|
|
1165
|
+
rectangles covering the range's content (in a bidi-aware
|
|
1166
|
+
way) for non-empty ones.
|
|
1167
|
+
*/static forRange(view,className,range){if(range.empty){let pos=view.coordsAtPos(range.head,range.assoc||1);if(!pos)return[];let base=getBase(view);return[new RectangleMarker(className,pos.left-base.left,pos.top-base.top,null,pos.bottom-pos.top)]}return rectanglesForRange(view,className,range)}}class LayerView{constructor(view,layer){this.view=view,this.layer=layer,this.drawn=[],this.scaleX=1,this.scaleY=1,this.measureReq={read:this.measure.bind(this),write:this.draw.bind(this)},this.dom=view.scrollDOM.appendChild(document.createElement("div")),this.dom.classList.add("cm-layer"),layer.above&&this.dom.classList.add("cm-layer-above"),layer.class&&this.dom.classList.add(layer.class),this.scale(),this.dom.setAttribute("aria-hidden","true"),this.setOrder(view.state),view.requestMeasure(this.measureReq),layer.mount&&layer.mount(this.dom,view)}update(update){update.startState.facet(layerOrder)!=update.state.facet(layerOrder)&&this.setOrder(update.state),(this.layer.update(update,this.dom)||update.geometryChanged)&&(this.scale(),update.view.requestMeasure(this.measureReq))}docViewUpdate(view){!1!==this.layer.updateOnDocViewUpdate&&view.requestMeasure(this.measureReq)}setOrder(state){let pos=0,order=state.facet(layerOrder);for(;pos<order.length&&order[pos]!=this.layer;)pos++;this.dom.style.zIndex=(this.layer.above?150:-1)-pos+""}measure(){return this.layer.markers(this.view)}scale(){let{scaleX,scaleY}=this.view;(scaleX!=this.scaleX||scaleY!=this.scaleY)&&(this.scaleX=scaleX,this.scaleY=scaleY,this.dom.style.transform=`scale(${1/scaleX}, ${1/scaleY})`)}draw(markers){if(markers.length!=this.drawn.length||markers.some((p,i)=>!sameMarker(p,this.drawn[i]))){let old=this.dom.firstChild,oldI=0;for(let marker of markers)marker.update&&old&&marker.constructor&&this.drawn[oldI].constructor&&marker.update(old,this.drawn[oldI])?(old=old.nextSibling,oldI++):this.dom.insertBefore(marker.draw(),old);for(;old;){let next=old.nextSibling;old.remove(),old=next}this.drawn=markers,browser.webkit&&(// Issue #1600, 1627, 1686
|
|
1168
|
+
this.dom.style.display=this.dom.firstChild?"":"none")}}destroy(){this.layer.destroy&&this.layer.destroy(this.dom,this.view),this.dom.remove()}}const layerOrder=state.Facet.define(),selectionConfig=state.Facet.define({combine(configs){return state.combineConfig(configs,{cursorBlinkRate:1200,drawRangeCursor:!0,iosSelectionHandles:!0},{cursorBlinkRate:(a,b)=>Math.min(a,b),drawRangeCursor:(a,b)=>a||b})}}),cursorLayer=layer({above:!0,markers(view){let{state:state$1}=view,conf=state$1.facet(selectionConfig),cursors=[];for(let r of state$1.selection.ranges){let prim=r==state$1.selection.main;if(r.empty||conf.drawRangeCursor&&!(prim&&browser.ios&&conf.iosSelectionHandles)){let className=prim?"cm-cursor cm-cursor-primary":"cm-cursor cm-cursor-secondary",cursor=r.empty?r:state.EditorSelection.cursor(r.head,r.assoc);for(let piece of RectangleMarker.forRange(view,className,cursor))cursors.push(piece)}}return cursors},update(update,dom){update.transactions.some(tr=>tr.selection)&&(dom.style.animationName="cm-blink"==dom.style.animationName?"cm-blink2":"cm-blink");let confChange=configChanged(update);return confChange&&setBlinkRate(update.state,dom),update.docChanged||update.selectionSet||confChange},mount(dom,view){setBlinkRate(view.state,dom)},class:"cm-cursorLayer"}),selectionLayer=layer({above:!1,markers(view){let markers=[],{main,ranges}=view.state.selection;for(let r of ranges)if(!r.empty)for(let marker of RectangleMarker.forRange(view,"cm-selectionBackground",r))markers.push(marker);if(browser.ios&&!main.empty&&view.state.facet(selectionConfig).iosSelectionHandles){for(let piece of RectangleMarker.forRange(view,"cm-selectionHandle cm-selectionHandle-start",state.EditorSelection.cursor(main.from,1)))markers.push(piece);for(let piece of RectangleMarker.forRange(view,"cm-selectionHandle cm-selectionHandle-end",state.EditorSelection.cursor(main.to,1)))markers.push(piece)}return markers},update(update,dom){return update.docChanged||update.selectionSet||update.viewportChanged||configChanged(update)},class:"cm-selectionLayer"}),hideNativeSelection=state.Prec.highest(EditorView.theme({".cm-line":{"& ::selection, &::selection":{backgroundColor:"transparent !important"},caretColor:"transparent !important"},".cm-content":{caretColor:"transparent !important","& :focus":{caretColor:"initial !important","&::selection, & ::selection":{backgroundColor:"Highlight !important"}}}})),setDropCursorPos=state.StateEffect.define({map(pos,mapping){return null==pos?null:mapping.mapPos(pos)}}),dropCursorPos=state.StateField.define({create(){return null},update(pos,tr){return null!=pos&&(pos=tr.changes.mapPos(pos)),tr.effects.reduce((pos,e)=>e.is(setDropCursorPos)?e.value:pos,pos)}}),drawDropCursor=ViewPlugin.fromClass(class{constructor(view){this.view=view,this.cursor=null,this.measureReq={read:this.readPos.bind(this),write:this.drawCursor.bind(this)}}update(update){var _a;let cursorPos=update.state.field(dropCursorPos);null==cursorPos?null!=this.cursor&&(null===(_a=this.cursor)||void 0===_a?void 0:_a.remove(),this.cursor=null):(!this.cursor&&(this.cursor=this.view.scrollDOM.appendChild(document.createElement("div")),this.cursor.className="cm-dropCursor"),(update.startState.field(dropCursorPos)!=cursorPos||update.docChanged||update.geometryChanged)&&this.view.requestMeasure(this.measureReq))}readPos(){let{view}=this,pos=view.state.field(dropCursorPos),rect=null!=pos&&view.coordsAtPos(pos);if(!rect)return null;let outer=view.scrollDOM.getBoundingClientRect();return{left:rect.left-outer.left+view.scrollDOM.scrollLeft*view.scaleX,top:rect.top-outer.top+view.scrollDOM.scrollTop*view.scaleY,height:rect.bottom-rect.top}}drawCursor(pos){if(this.cursor){let{scaleX,scaleY}=this.view;pos?(this.cursor.style.left=pos.left/scaleX+"px",this.cursor.style.top=pos.top/scaleY+"px",this.cursor.style.height=pos.height/scaleY+"px"):this.cursor.style.left="-100000px"}}destroy(){this.cursor&&this.cursor.remove()}setDropPos(pos){this.view.state.field(dropCursorPos)!=pos&&this.view.dispatch({effects:setDropCursorPos.of(pos)})}},{eventObservers:{dragover(event){this.setDropPos(this.view.posAtCoords({x:event.clientX,y:event.clientY}))},dragleave(event){event.target!=this.view.contentDOM&&this.view.contentDOM.contains(event.relatedTarget)||this.setDropPos(null)},dragend(){this.setDropPos(null)},drop(){this.setDropPos(null)}}});class MatchDecorator{/**
|
|
1169
|
+
Create a decorator.
|
|
1170
|
+
*/constructor(config){const{regexp,decoration,decorate,boundary,maxLength=1e3}=config;if(!regexp.global)throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");if(this.regexp=regexp,decorate)this.addMatch=(match,view,from,add)=>decorate(add,from,from+match[0].length,match,view);else if("function"==typeof decoration)this.addMatch=(match,view,from,add)=>{let deco=decoration(match,view,from);deco&&add(from,from+match[0].length,deco)};else if(decoration)this.addMatch=(match,_view,from,add)=>add(from,from+match[0].length,decoration);else throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator");this.boundary=boundary,this.maxLength=maxLength}/**
|
|
1171
|
+
Compute the full set of decorations for matches in the given
|
|
1172
|
+
view's viewport. You'll want to call this when initializing your
|
|
1173
|
+
plugin.
|
|
1174
|
+
*/createDeco(view){let build=new state.RangeSetBuilder,add=build.add.bind(build);for(let{from,to}of matchRanges(view,this.maxLength))iterMatches(view.state.doc,this.regexp,from,to,(from,m)=>this.addMatch(m,view,from,add));return build.finish()}/**
|
|
1175
|
+
Update a set of decorations for a view update. `deco` _must_ be
|
|
1176
|
+
the set of decorations produced by _this_ `MatchDecorator` for
|
|
1177
|
+
the view state before the update.
|
|
1178
|
+
*/updateDeco(update,deco){let changeFrom=1e9,changeTo=-1;return update.docChanged&&update.changes.iterChanges((_f,_t,from,to)=>{to>=update.view.viewport.from&&from<=update.view.viewport.to&&(changeFrom=Math.min(from,changeFrom),changeTo=Math.max(to,changeTo))}),update.viewportMoved||1e3<changeTo-changeFrom?this.createDeco(update.view):-1<changeTo?this.updateRange(update.view,deco.map(update.changes),changeFrom,changeTo):deco}updateRange(view,deco,updateFrom,updateTo){for(let r of view.visibleRanges){let from=Math.max(r.from,updateFrom),to=Math.min(r.to,updateTo);if(to>=from){let fromLine=view.state.doc.lineAt(from),toLine=fromLine.to<to?view.state.doc.lineAt(to):fromLine,start=Math.max(r.from,fromLine.from),end=Math.min(r.to,toLine.to);if(this.boundary){for(;from>fromLine.from;from--)if(this.boundary.test(fromLine.text[from-1-fromLine.from])){start=from;break}for(;to<toLine.to;to++)if(this.boundary.test(toLine.text[to-toLine.from])){end=to;break}}let ranges=[],add=(from,to,deco)=>ranges.push(deco.range(from,to)),m;if(fromLine==toLine)for(this.regexp.lastIndex=start-fromLine.from;(m=this.regexp.exec(fromLine.text))&&m.index<end-fromLine.from;)this.addMatch(m,view,m.index+fromLine.from,add);else iterMatches(view.state.doc,this.regexp,start,end,(from,m)=>this.addMatch(m,view,from,add));deco=deco.update({filterFrom:start,filterTo:end,filter:(from,to)=>from<start||to>end,add:ranges})}}return deco}}const UnicodeRegexpSupport=null==/x/.unicode?"g":"gu",Specials=new RegExp("[\0-\b\n-\x1F\x7F-\x9F\xAD\u061C\u200B\u200E\u200F\u2028\u2029\u202D\u202E\u2066\u2067\u2069\uFEFF\uFFF9-\uFFFC]",UnicodeRegexpSupport),Names={0:"null",7:"bell",8:"backspace",10:"newline",11:"vertical tab",13:"carriage return",27:"escape",8203:"zero width space",8204:"zero width non-joiner",8205:"zero width joiner",8206:"left-to-right mark",8207:"right-to-left mark",8232:"line separator",8237:"left-to-right override",8238:"right-to-left override",8294:"left-to-right isolate",8295:"right-to-left isolate",8297:"pop directional isolate",8233:"paragraph separator",65279:"zero width no-break space",65532:"object replacement"};let _supportsTabSize=null;const specialCharConfig=state.Facet.define({combine(configs){let config=state.combineConfig(configs,{render:null,specialChars:Specials,addSpecialChars:null});return(config.replaceTabs=!supportsTabSize())&&(config.specialChars=new RegExp("\t|"+config.specialChars.source,UnicodeRegexpSupport)),config.addSpecialChars&&(config.specialChars=new RegExp(config.specialChars.source+"|"+config.addSpecialChars.source,UnicodeRegexpSupport)),config}});let _plugin=null;const DefaultPlaceholder="\u2022";class SpecialCharWidget extends WidgetType{constructor(options,code){super(),this.options=options,this.code=code}eq(other){return other.code==this.code}toDOM(view){let ph=placeholder$1(this.code),desc=view.state.phrase("Control character")+" "+(Names[this.code]||"0x"+this.code.toString(16)),custom=this.options.render&&this.options.render(this.code,desc,ph);if(custom)return custom;let span=document.createElement("span");return span.textContent=ph,span.title=desc,span.setAttribute("aria-label",desc),span.className="cm-specialChar",span}ignoreEvent(){return!1}}class TabWidget extends WidgetType{constructor(width){super(),this.width=width}eq(other){return other.width==this.width}toDOM(){let span=document.createElement("span");return span.textContent="\t",span.className="cm-tab",span.style.width=this.width+"px",span}ignoreEvent(){return!1}}const plugin=ViewPlugin.fromClass(class{constructor(){this.height=1e3,this.attrs={style:"padding-bottom: 1000px"}}update(update){let{view}=update,height=view.viewState.editorHeight-view.defaultLineHeight-view.documentPadding.top-.5;0<=height&&height!=this.height&&(this.height=height,this.attrs={style:`padding-bottom: ${height}px`})}}),lineDeco=Decoration.line({class:"cm-activeLine"}),activeLineHighlighter=ViewPlugin.fromClass(class{constructor(view){this.decorations=this.getDeco(view)}update(update){(update.docChanged||update.selectionSet)&&(this.decorations=this.getDeco(update.view))}getDeco(view){let lastLineStart=-1,deco=[];for(let r of view.state.selection.ranges){let line=view.lineBlockAt(r.head);line.from>lastLineStart&&(deco.push(lineDeco.range(line.from)),lastLineStart=line.from)}return Decoration.set(deco)}},{decorations:v=>v.decorations});class Placeholder extends WidgetType{constructor(content){super(),this.content=content}toDOM(view){let wrap=document.createElement("span");return wrap.className="cm-placeholder",wrap.style.pointerEvents="none",wrap.appendChild("string"==typeof this.content?document.createTextNode(this.content):"function"==typeof this.content?this.content(view):this.content.cloneNode(!0)),wrap.setAttribute("aria-hidden","true"),wrap}coordsAt(dom){let rects=dom.firstChild?clientRectsFor(dom.firstChild):[];if(!rects.length)return null;let style=window.getComputedStyle(dom.parentNode),rect=flattenRect(rects[0],"rtl"!=style.direction),lineHeight=parseInt(style.lineHeight);return rect.bottom-rect.top>1.5*lineHeight?{left:rect.left,right:rect.right,top:rect.top,bottom:rect.top+lineHeight}:rect}ignoreEvent(){return!1}}const MaxOff=2e3,keys={Alt:[18,e=>!!e.altKey],Control:[17,e=>!!e.ctrlKey],Shift:[16,e=>!!e.shiftKey],Meta:[91,e=>!!e.metaKey]},showCrosshair={style:"cursor: crosshair"},Outside="-10000px";class TooltipViewManager{constructor(view,facet,createTooltipView,removeTooltipView){this.facet=facet,this.createTooltipView=createTooltipView,this.removeTooltipView=removeTooltipView,this.input=view.state.facet(facet),this.tooltips=this.input.filter(t=>t);let prev=null;this.tooltipViews=this.tooltips.map(t=>prev=createTooltipView(t,prev))}update(update,above){var _a;let input=update.state.facet(this.facet),tooltips=input.filter(x=>x);if(input===this.input){for(let t of this.tooltipViews)t.update&&t.update(update);return!1}let tooltipViews=[],newAbove=above?[]:null;for(let i=0;i<tooltips.length;i++){let tip=tooltips[i],known=-1;if(tip){for(let i=0,other;i<this.tooltips.length;i++)other=this.tooltips[i],other&&other.create==tip.create&&(known=i);if(0>known)tooltipViews[i]=this.createTooltipView(tip,i?tooltipViews[i-1]:null),newAbove&&(newAbove[i]=!!tip.above);else{let tooltipView=tooltipViews[i]=this.tooltipViews[known];newAbove&&(newAbove[i]=above[known]),tooltipView.update&&tooltipView.update(update)}}}for(let t of this.tooltipViews)0>tooltipViews.indexOf(t)&&(this.removeTooltipView(t),null===(_a=t.destroy)||void 0===_a?void 0:_a.call(t));return above&&(newAbove.forEach((val,i)=>above[i]=val),above.length=newAbove.length),this.input=input,this.tooltips=tooltips,this.tooltipViews=tooltipViews,!0}}const tooltipConfig=state.Facet.define({combine:values=>{var _a,_b,_c;return{position:browser.ios?"absolute":(null===(_a=values.find(conf=>conf.position))||void 0===_a?void 0:_a.position)||"fixed",parent:(null===(_b=values.find(conf=>conf.parent))||void 0===_b?void 0:_b.parent)||null,tooltipSpace:(null===(_c=values.find(conf=>conf.tooltipSpace))||void 0===_c?void 0:_c.tooltipSpace)||windowSpace}}}),knownHeight=new WeakMap,tooltipPlugin=ViewPlugin.fromClass(class{constructor(view){this.view=view,this.above=[],this.inView=!0,this.madeAbsolute=!1,this.lastTransaction=0,this.measureTimeout=-1;let config=view.state.facet(tooltipConfig);this.position=config.position,this.parent=config.parent,this.classes=view.themeClasses,this.createContainer(),this.measureReq={read:this.readMeasure.bind(this),write:this.writeMeasure.bind(this),key:this},this.resizeObserver="function"==typeof ResizeObserver?new ResizeObserver(()=>this.measureSoon()):null,this.manager=new TooltipViewManager(view,showTooltip,(t,p)=>this.createTooltip(t,p),t=>{this.resizeObserver&&this.resizeObserver.unobserve(t.dom),t.dom.remove()}),this.above=this.manager.tooltips.map(t=>!!t.above),this.intersectionObserver="function"==typeof IntersectionObserver?new IntersectionObserver(entries=>{Date.now()>this.lastTransaction-50&&0<entries.length&&1>entries[entries.length-1].intersectionRatio&&this.measureSoon()},{threshold:[1]}):null,this.observeIntersection(),view.win.addEventListener("resize",this.measureSoon=this.measureSoon.bind(this)),this.maybeMeasure()}createContainer(){this.parent?(this.container=document.createElement("div"),this.container.style.position="relative",this.container.className=this.view.themeClasses,this.parent.appendChild(this.container)):this.container=this.view.dom}observeIntersection(){if(this.intersectionObserver){this.intersectionObserver.disconnect();for(let tooltip of this.manager.tooltipViews)this.intersectionObserver.observe(tooltip.dom)}}measureSoon(){0>this.measureTimeout&&(this.measureTimeout=setTimeout(()=>{this.measureTimeout=-1,this.maybeMeasure()},50))}update(update){update.transactions.length&&(this.lastTransaction=Date.now());let updated=this.manager.update(update,this.above);updated&&this.observeIntersection();let shouldMeasure=updated||update.geometryChanged,newConfig=update.state.facet(tooltipConfig);if(newConfig.position!=this.position&&!this.madeAbsolute){this.position=newConfig.position;for(let t of this.manager.tooltipViews)t.dom.style.position=this.position;shouldMeasure=!0}if(newConfig.parent!=this.parent){this.parent&&this.container.remove(),this.parent=newConfig.parent,this.createContainer();for(let t of this.manager.tooltipViews)this.container.appendChild(t.dom);shouldMeasure=!0}else this.parent&&this.view.themeClasses!=this.classes&&(this.classes=this.container.className=this.view.themeClasses);shouldMeasure&&this.maybeMeasure()}createTooltip(tooltip,prev){let tooltipView=tooltip.create(this.view),before=prev?prev.dom:null;if(tooltipView.dom.classList.add("cm-tooltip"),tooltip.arrow&&!tooltipView.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")){let arrow=document.createElement("div");arrow.className="cm-tooltip-arrow",tooltipView.dom.appendChild(arrow)}return tooltipView.dom.style.position=this.position,tooltipView.dom.style.top=Outside,tooltipView.dom.style.left="0px",this.container.insertBefore(tooltipView.dom,before),tooltipView.mount&&tooltipView.mount(this.view),this.resizeObserver&&this.resizeObserver.observe(tooltipView.dom),tooltipView}destroy(){var _a,_b,_c;this.view.win.removeEventListener("resize",this.measureSoon);for(let tooltipView of this.manager.tooltipViews)tooltipView.dom.remove(),null===(_a=tooltipView.destroy)||void 0===_a?void 0:_a.call(tooltipView);this.parent&&this.container.remove(),null===(_b=this.resizeObserver)||void 0===_b?void 0:_b.disconnect(),null===(_c=this.intersectionObserver)||void 0===_c?void 0:_c.disconnect(),clearTimeout(this.measureTimeout)}readMeasure(){let scaleX=1,scaleY=1,makeAbsolute=!1;if("fixed"==this.position&&this.manager.tooltipViews.length){let{dom}=this.manager.tooltipViews[0];if(browser.safari){// Safari always sets offsetParent to null, even if a fixed
|
|
1179
|
+
// element is positioned relative to a transformed parent. So
|
|
1180
|
+
// we use this kludge to try and detect this.
|
|
1181
|
+
let rect=dom.getBoundingClientRect();makeAbsolute=1<Math.abs(rect.top+1e4)||1<Math.abs(rect.left)}else// More conforming browsers will set offsetParent to the
|
|
1182
|
+
// transformed element.
|
|
1183
|
+
makeAbsolute=!!dom.offsetParent&&dom.offsetParent!=this.container.ownerDocument.body}if(makeAbsolute||"absolute"==this.position)if(this.parent){let rect=this.parent.getBoundingClientRect();rect.width&&rect.height&&(scaleX=rect.width/this.parent.offsetWidth,scaleY=rect.height/this.parent.offsetHeight)}else({scaleX,scaleY}=this.view.viewState);let visible=this.view.scrollDOM.getBoundingClientRect(),margins=getScrollMargins(this.view);return{visible:{left:visible.left+margins.left,top:visible.top+margins.top,right:visible.right-margins.right,bottom:visible.bottom-margins.bottom},parent:this.parent?this.container.getBoundingClientRect():this.view.dom.getBoundingClientRect(),pos:this.manager.tooltips.map((t,i)=>{let tv=this.manager.tooltipViews[i];return tv.getCoords?tv.getCoords(t.pos):this.view.coordsAtPos(t.pos)}),size:this.manager.tooltipViews.map(({dom})=>dom.getBoundingClientRect()),space:this.view.state.facet(tooltipConfig).tooltipSpace(this.view),scaleX,scaleY,makeAbsolute}}writeMeasure(measured){var _a;if(measured.makeAbsolute){this.madeAbsolute=!0,this.position="absolute";for(let t of this.manager.tooltipViews)t.dom.style.position="absolute"}let{visible,space,scaleX,scaleY}=measured,others=[];for(let i=0;i<this.manager.tooltips.length;i++){let tooltip=this.manager.tooltips[i],tView=this.manager.tooltipViews[i],{dom}=tView,pos=measured.pos[i],size=measured.size[i];// Hide tooltips that are outside of the editor.
|
|
1184
|
+
if(!pos||!1!==tooltip.clip&&(pos.bottom<=Math.max(visible.top,space.top)||pos.top>=Math.min(visible.bottom,space.bottom)||pos.right<Math.max(visible.left,space.left)-.1||pos.left>Math.min(visible.right,space.right)+.1)){dom.style.top=Outside;continue}let arrow=tooltip.arrow?tView.dom.querySelector(".cm-tooltip-arrow"):null,arrowHeight=arrow?7/* Arrow.Size */:0,width=size.right-size.left,height=null!==(_a=knownHeight.get(tView))&&void 0!==_a?_a:size.bottom-size.top,offset=tView.offset||noOffset,ltr=this.view.textDirection==exports.Direction.LTR,left=size.width>space.right-space.left?ltr?space.left:space.right-size.width:ltr?Math.max(space.left,Math.min(pos.left-(arrow?14/* Arrow.Offset */:0)+offset.x,space.right-width)):Math.min(Math.max(space.left,pos.left-width+(arrow?14/* Arrow.Offset */:0)-offset.x),space.right-width),above=this.above[i];!tooltip.strictSide&&(above?pos.top-height-arrowHeight-offset.y<space.top:pos.bottom+height+arrowHeight+offset.y>space.bottom)&&above==space.bottom-pos.bottom>pos.top-space.top&&(above=this.above[i]=!above);let spaceVert=(above?pos.top-space.top:space.bottom-pos.bottom)-arrowHeight;if(spaceVert<height&&!1!==tView.resize){if(spaceVert<this.view.defaultLineHeight){dom.style.top=Outside;continue}knownHeight.set(tView,height),dom.style.height=(height=spaceVert)/scaleY+"px"}else dom.style.height&&(dom.style.height="");let top=above?pos.top-height-arrowHeight-offset.y:pos.bottom+arrowHeight+offset.y,right=left+width;if(!0!==tView.overlap)for(let r of others)r.left<right&&r.right>left&&r.top<top+height&&r.bottom>top&&(top=above?r.top-height-2-arrowHeight:r.bottom+arrowHeight+2);if("absolute"==this.position?(dom.style.top=(top-measured.parent.top)/scaleY+"px",setLeftStyle(dom,(left-measured.parent.left)/scaleX)):(dom.style.top=top/scaleY+"px",setLeftStyle(dom,left/scaleX)),arrow){let arrowLeft=pos.left+(ltr?offset.x:-offset.x)-(left+14/* Arrow.Offset */-7/* Arrow.Size */);arrow.style.left=arrowLeft/scaleX+"px"}!0!==tView.overlap&&others.push({left,top,right,bottom:top+height}),dom.classList.toggle("cm-tooltip-above",above),dom.classList.toggle("cm-tooltip-below",!above),tView.positioned&&tView.positioned(measured.space)}}maybeMeasure(){if(this.manager.tooltips.length&&(this.view.inView&&this.view.requestMeasure(this.measureReq),this.inView!=this.view.inView&&(this.inView=this.view.inView,!this.inView)))for(let tv of this.manager.tooltipViews)tv.dom.style.top=Outside}},{eventObservers:{scroll(){this.maybeMeasure()}}}),baseTheme=EditorView.baseTheme({".cm-tooltip":{zIndex:500,boxSizing:"border-box"},"&light .cm-tooltip":{border:"1px solid #bbb",backgroundColor:"#f5f5f5"},"&light .cm-tooltip-section:not(:first-child)":{borderTop:"1px solid #bbb"},"&dark .cm-tooltip":{backgroundColor:"#333338",color:"white"},".cm-tooltip-arrow":{height:`${7/* Arrow.Size */}px`,width:`${14}px`,position:"absolute",zIndex:-1,overflow:"hidden","&:before, &:after":{content:"''",position:"absolute",width:0,height:0,borderLeft:`${7/* Arrow.Size */}px solid transparent`,borderRight:`${7/* Arrow.Size */}px solid transparent`},".cm-tooltip-above &":{bottom:`-${7/* Arrow.Size */}px`,"&:before":{borderTop:`${7/* Arrow.Size */}px solid #bbb`},"&:after":{borderTop:`${7/* Arrow.Size */}px solid #f5f5f5`,bottom:"1px"}},".cm-tooltip-below &":{top:`-${7/* Arrow.Size */}px`,"&:before":{borderBottom:`${7/* Arrow.Size */}px solid #bbb`},"&:after":{borderBottom:`${7/* Arrow.Size */}px solid #f5f5f5`,top:"1px"}}},"&dark .cm-tooltip .cm-tooltip-arrow":{"&:before":{borderTopColor:"#333338",borderBottomColor:"#333338"},"&:after":{borderTopColor:"transparent",borderBottomColor:"transparent"}}}),noOffset={x:0,y:0},showTooltip=state.Facet.define({enables:[tooltipPlugin,baseTheme]}),showHoverTooltip=state.Facet.define({combine:inputs=>inputs.reduce((a,i)=>a.concat(i),[])});/**
|
|
1185
|
+
Facet to which an extension can add a value to show a tooltip.
|
|
1186
|
+
*/class HoverTooltipHost{// Needs to be static so that host tooltip instances always match
|
|
1187
|
+
static create(view){return new HoverTooltipHost(view)}constructor(view){this.view=view,this.mounted=!1,this.dom=document.createElement("div"),this.dom.classList.add("cm-tooltip-hover"),this.manager=new TooltipViewManager(view,showHoverTooltip,(t,p)=>this.createHostedView(t,p),t=>t.dom.remove())}createHostedView(tooltip,prev){let hostedView=tooltip.create(this.view);return hostedView.dom.classList.add("cm-tooltip-section"),this.dom.insertBefore(hostedView.dom,prev?prev.dom.nextSibling:this.dom.firstChild),this.mounted&&hostedView.mount&&hostedView.mount(this.view),hostedView}mount(view){for(let hostedView of this.manager.tooltipViews)hostedView.mount&&hostedView.mount(view);this.mounted=!0}positioned(space){for(let hostedView of this.manager.tooltipViews)hostedView.positioned&&hostedView.positioned(space)}update(update){this.manager.update(update)}destroy(){var _a;for(let t of this.manager.tooltipViews)null===(_a=t.destroy)||void 0===_a?void 0:_a.call(t)}passProp(name){let value;for(let view of this.manager.tooltipViews){let given=view[name];if(void 0!==given)if(void 0===value)value=given;else if(value!==given)return}return value}get offset(){return this.passProp("offset")}get getCoords(){return this.passProp("getCoords")}get overlap(){return this.passProp("overlap")}get resize(){return this.passProp("resize")}}const showHoverTooltipHost=showTooltip.compute([showHoverTooltip],state=>{let tooltips=state.facet(showHoverTooltip);return 0===tooltips.length?null:{pos:Math.min(...tooltips.map(t=>t.pos)),end:Math.max(...tooltips.map(t=>{var _a;return null!==(_a=t.end)&&void 0!==_a?_a:t.pos})),create:HoverTooltipHost.create,above:tooltips[0].above,arrow:tooltips.some(t=>t.arrow)}}),hoverPlugin=state.Facet.define();class HoverPlugin{constructor(view,source,field,locked,setHover,hoverTime){this.view=view,this.source=source,this.field=field,this.locked=locked,this.setHover=setHover,this.hoverTime=hoverTime,this.hoverTimeout=-1,this.restartTimeout=-1,this.pending=null,this.lastMove={x:0,y:0,target:view.dom,time:0},this.checkHover=this.checkHover.bind(this),view.dom.addEventListener("mouseleave",this.mouseleave=this.mouseleave.bind(this)),view.dom.addEventListener("mousemove",this.mousemove=this.mousemove.bind(this))}update(update){this.pending&&(this.pending=null,clearTimeout(this.restartTimeout),this.restartTimeout=setTimeout(()=>this.startHover(),20))}get active(){return this.view.state.field(this.field)}checkHover(){if(this.hoverTimeout=-1,!this.active.length){let hovered=Date.now()-this.lastMove.time;hovered<this.hoverTime?this.hoverTimeout=setTimeout(this.checkHover,this.hoverTime-hovered):this.startHover()}}startHover(){clearTimeout(this.restartTimeout);let{view,lastMove}=this,tile=view.docView.tile.nearest(lastMove.target);if(!tile)return;let side=1,pos;if(tile.isWidget())pos=tile.posAtStart;else{if(pos=view.posAtCoords(lastMove),null==pos)return;let posCoords=view.coordsAtPos(pos);if(!posCoords||lastMove.y<posCoords.top||lastMove.y>posCoords.bottom||lastMove.x<posCoords.left-view.defaultCharacterWidth||lastMove.x>posCoords.right+view.defaultCharacterWidth)return;let bidi=view.bidiSpans(view.state.doc.lineAt(pos)).find(s=>s.from<=pos&&s.to>=pos),rtl=bidi&&bidi.dir==exports.Direction.RTL?-1:1;side=lastMove.x<posCoords.left?-rtl:rtl}this.activateHover(view,pos,side)}activateHover(view,pos,side,locked){let open=this.source(view,pos,side),done=value=>{if(value&&(!Array.isArray(value)||value.length)){let tooltips=Array.isArray(value)?value:[value];locked&&this.locked.set(tooltips,locked),view.dispatch({effects:this.setHover.of(tooltips)})}};if(open&&"then"in open){let pending=this.pending={pos};open.then(result=>{this.pending==pending&&(this.pending=null,done(result))},e=>logException(view.state,e,"hover tooltip"))}else done(open)}get tooltip(){let plugin=this.view.plugin(tooltipPlugin),index=plugin?plugin.manager.tooltips.findIndex(t=>t.create==HoverTooltipHost.create):-1;return-1<index?plugin.manager.tooltipViews[index]:null}mousemove(event){var _a,_b;this.lastMove={x:event.clientX,y:event.clientY,target:event.target,time:Date.now()},0>this.hoverTimeout&&(this.hoverTimeout=setTimeout(this.checkHover,this.hoverTime));let{active,tooltip}=this;if(active.length&&!this.locked.has(active)&&tooltip&&!isInTooltip(tooltip.dom,event)||this.pending){let{pos}=active[0]||this.pending,end=null!==(_b=null===(_a=active[0])||void 0===_a?void 0:_a.end)&&void 0!==_b?_b:pos;(pos==end?this.view.posAtCoords(this.lastMove)!=pos:!isOverRange(this.view,pos,end,event.clientX,event.clientY))&&(this.view.dispatch({effects:this.setHover.of([])}),this.pending=null)}}mouseleave(event){clearTimeout(this.hoverTimeout),this.hoverTimeout=-1;let{active}=this;if(active.length&&!this.locked.has(active)){let{tooltip}=this,inTooltip=tooltip&&tooltip.dom.contains(event.relatedTarget);inTooltip?this.watchTooltipLeave(tooltip.dom):this.view.dispatch({effects:this.setHover.of([])})}}watchTooltipLeave(tooltip){let watch=event=>{tooltip.removeEventListener("mouseleave",watch);let{active}=this;!active.length||this.locked.has(active)||this.view.dom.contains(event.relatedTarget)||this.view.dispatch({effects:this.setHover.of([])})};tooltip.addEventListener("mouseleave",watch)}destroy(){clearTimeout(this.hoverTimeout),clearTimeout(this.restartTimeout),this.view.dom.removeEventListener("mouseleave",this.mouseleave),this.view.dom.removeEventListener("mousemove",this.mousemove)}}const tooltipMargin=4,closeHoverTooltipEffect=state.StateEffect.define(),closeHoverTooltips=closeHoverTooltipEffect.of(null),panelConfig=state.Facet.define({combine(configs){let topContainer,bottomContainer;for(let c of configs)topContainer=topContainer||c.topContainer,bottomContainer=bottomContainer||c.bottomContainer;return{topContainer,bottomContainer}}}),panelPlugin=ViewPlugin.fromClass(class{constructor(view){this.input=view.state.facet(showPanel),this.specs=this.input.filter(s=>s),this.panels=this.specs.map(spec=>spec(view));let conf=view.state.facet(panelConfig);this.top=new PanelGroup(view,!0,conf.topContainer),this.bottom=new PanelGroup(view,!1,conf.bottomContainer),this.top.sync(this.panels.filter(p=>p.top)),this.bottom.sync(this.panels.filter(p=>!p.top));for(let p of this.panels)p.dom.classList.add("cm-panel"),p.mount&&p.mount()}update(update){let conf=update.state.facet(panelConfig);this.top.container!=conf.topContainer&&(this.top.sync([]),this.top=new PanelGroup(update.view,!0,conf.topContainer)),this.bottom.container!=conf.bottomContainer&&(this.bottom.sync([]),this.bottom=new PanelGroup(update.view,!1,conf.bottomContainer)),this.top.syncClasses(),this.bottom.syncClasses();let input=update.state.facet(showPanel);if(input!=this.input){let specs=input.filter(x=>x),panels=[],top=[],bottom=[],mount=[];for(let spec of specs){let known=this.specs.indexOf(spec),panel;0>known?(panel=spec(update.view),mount.push(panel)):(panel=this.panels[known],panel.update&&panel.update(update)),panels.push(panel),(panel.top?top:bottom).push(panel)}this.specs=specs,this.panels=panels,this.top.sync(top),this.bottom.sync(bottom);for(let p of mount)p.dom.classList.add("cm-panel"),p.mount&&p.mount()}else for(let p of this.panels)p.update&&p.update(update)}destroy(){this.top.sync([]),this.bottom.sync([])}},{provide:plugin=>EditorView.scrollMargins.of(view=>{let value=view.plugin(plugin);return value&&{top:value.top.scrollMargin(),bottom:value.bottom.scrollMargin()}})});/**
|
|
1188
|
+
Transaction effect that closes all hover tooltips.
|
|
1189
|
+
*/class PanelGroup{constructor(view,top,container){this.view=view,this.top=top,this.container=container,this.dom=void 0,this.classes="",this.panels=[],this.syncClasses()}sync(panels){for(let p of this.panels)p.destroy&&0>panels.indexOf(p)&&p.destroy();this.panels=panels,this.syncDOM()}syncDOM(){if(0==this.panels.length)return void(this.dom&&(this.dom.remove(),this.dom=void 0));if(!this.dom){this.dom=document.createElement("div"),this.dom.className=this.top?"cm-panels cm-panels-top":"cm-panels cm-panels-bottom",this.dom.style[this.top?"top":"bottom"]="0";let parent=this.container||this.view.dom;parent.insertBefore(this.dom,this.top?parent.firstChild:null)}let curDOM=this.dom.firstChild;for(let panel of this.panels)if(panel.dom.parentNode==this.dom){for(;curDOM!=panel.dom;)curDOM=rm(curDOM);curDOM=curDOM.nextSibling}else this.dom.insertBefore(panel.dom,curDOM);for(;curDOM;)curDOM=rm(curDOM)}scrollMargin(){return!this.dom||this.container?0:Math.max(0,this.top?this.dom.getBoundingClientRect().bottom-Math.max(0,this.view.scrollDOM.getBoundingClientRect().top):Math.min(innerHeight,this.view.scrollDOM.getBoundingClientRect().bottom)-this.dom.getBoundingClientRect().top)}syncClasses(){if(this.container&&this.classes!=this.view.themeClasses){for(let cls of this.classes.split(" "))cls&&this.container.classList.remove(cls);for(let cls of(this.classes=this.view.themeClasses).split(" "))cls&&this.container.classList.add(cls)}}}const showPanel=state.Facet.define({enables:panelPlugin}),dialogField=state.StateField.define({create(){return[]},update(dialogs,tr){for(let e of tr.effects)e.is(openDialogEffect)?dialogs=[e.value].concat(dialogs):e.is(closeDialogEffect)&&(dialogs=dialogs.filter(d=>d!=e.value));return dialogs},provide:f=>showPanel.computeN([f],state=>state.field(f))}),openDialogEffect=state.StateEffect.define(),closeDialogEffect=state.StateEffect.define();class GutterMarker extends state.RangeValue{/**
|
|
1190
|
+
@internal
|
|
1191
|
+
*/compare(other){return this==other||this.constructor==other.constructor&&this.eq(other)}/**
|
|
1192
|
+
Compare this marker to another marker of the same type.
|
|
1193
|
+
*/eq(other){return!1}/**
|
|
1194
|
+
Called if the marker has a `toDOM` method and its representation
|
|
1195
|
+
was removed from a gutter.
|
|
1196
|
+
*/destroy(dom){}}GutterMarker.prototype.elementClass="",GutterMarker.prototype.toDOM=void 0,GutterMarker.prototype.mapMode=state.MapMode.TrackBefore,GutterMarker.prototype.startSide=GutterMarker.prototype.endSide=-1,GutterMarker.prototype.point=!0;/**
|
|
1197
|
+
Facet used to add a class to all gutter elements for a given line.
|
|
1198
|
+
Markers given to this facet should _only_ define an
|
|
1199
|
+
[`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a
|
|
1200
|
+
[`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear
|
|
1201
|
+
in all gutters for the line).
|
|
1202
|
+
*/const gutterLineClass=state.Facet.define(),gutterWidgetClass=state.Facet.define(),defaults={class:"",renderEmptyElements:!1,elementStyle:"",markers:()=>state.RangeSet.empty,lineMarker:()=>null,widgetMarker:()=>null,lineMarkerChange:null,initialSpacer:null,updateSpacer:null,domEventHandlers:{},side:"before"},activeGutters=state.Facet.define(),unfixGutters=state.Facet.define({combine:values=>values.some(x=>x)}),gutterView=ViewPlugin.fromClass(class{constructor(view){this.view=view,this.domAfter=null,this.prevViewport=view.viewport,this.dom=document.createElement("div"),this.dom.className="cm-gutters cm-gutters-before",this.dom.setAttribute("aria-hidden","true"),this.dom.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.gutters=view.state.facet(activeGutters).map(conf=>new SingleGutterView(view,conf)),this.fixed=!view.state.facet(unfixGutters);for(let gutter of this.gutters)"after"==gutter.config.side?this.getDOMAfter().appendChild(gutter.dom):this.dom.appendChild(gutter.dom);this.fixed&&(this.dom.style.position="sticky"),this.syncGutters(!1),view.scrollDOM.insertBefore(this.dom,view.contentDOM)}getDOMAfter(){return this.domAfter||(this.domAfter=document.createElement("div"),this.domAfter.className="cm-gutters cm-gutters-after",this.domAfter.setAttribute("aria-hidden","true"),this.domAfter.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.domAfter.style.position=this.fixed?"sticky":"",this.view.scrollDOM.appendChild(this.domAfter)),this.domAfter}update(update){if(this.updateGutters(update)){// Detach during sync when the viewport changed significantly
|
|
1203
|
+
// (such as during scrolling), since for large updates that is
|
|
1204
|
+
// faster.
|
|
1205
|
+
let vpA=this.prevViewport,vpB=update.view.viewport,vpOverlap=Math.min(vpA.to,vpB.to)-Math.max(vpA.from,vpB.from);this.syncGutters(vpOverlap<.8*(vpB.to-vpB.from))}if(update.geometryChanged){let min=this.view.contentHeight/this.view.scaleY+"px";this.dom.style.minHeight=min,this.domAfter&&(this.domAfter.style.minHeight=min)}this.view.state.facet(unfixGutters)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?"sticky":"",this.domAfter&&(this.domAfter.style.position=this.fixed?"sticky":"")),this.prevViewport=update.view.viewport}syncGutters(detach){let after=this.dom.nextSibling;detach&&(this.dom.remove(),this.domAfter&&this.domAfter.remove());let lineClasses=state.RangeSet.iter(this.view.state.facet(gutterLineClass),this.view.viewport.from),classSet=[],contexts=this.gutters.map(gutter=>new UpdateContext(gutter,this.view.viewport,-this.view.documentPadding.top));for(let line of this.view.viewportLineBlocks)if(classSet.length&&(classSet=[]),Array.isArray(line.type)){let first=!0;for(let b of line.type)if(b.type==exports.BlockType.Text&&first){advanceCursor(lineClasses,classSet,b.from);for(let cx of contexts)cx.line(this.view,b,classSet);first=!1}else if(b.widget)for(let cx of contexts)cx.widget(this.view,b)}else if(line.type==exports.BlockType.Text){advanceCursor(lineClasses,classSet,line.from);for(let cx of contexts)cx.line(this.view,line,classSet)}else if(line.widget)for(let cx of contexts)cx.widget(this.view,line);for(let cx of contexts)cx.finish();detach&&(this.view.scrollDOM.insertBefore(this.dom,after),this.domAfter&&this.view.scrollDOM.appendChild(this.domAfter))}updateGutters(update){let prev=update.startState.facet(activeGutters),cur=update.state.facet(activeGutters),change=update.docChanged||update.heightChanged||update.viewportChanged||!state.RangeSet.eq(update.startState.facet(gutterLineClass),update.state.facet(gutterLineClass),update.view.viewport.from,update.view.viewport.to);if(prev==cur)for(let gutter of this.gutters)gutter.update(update)&&(change=!0);else{change=!0;let gutters=[];for(let conf of cur){let known=prev.indexOf(conf);0>known?gutters.push(new SingleGutterView(this.view,conf)):(this.gutters[known].update(update),gutters.push(this.gutters[known]))}for(let g of this.gutters)g.dom.remove(),0>gutters.indexOf(g)&&g.destroy();for(let g of gutters)"after"==g.config.side?this.getDOMAfter().appendChild(g.dom):this.dom.appendChild(g.dom);this.gutters=gutters}return change}destroy(){for(let view of this.gutters)view.destroy();this.dom.remove(),this.domAfter&&this.domAfter.remove()}},{provide:plugin=>EditorView.scrollMargins.of(view=>{let value=view.plugin(plugin);if(!value||0==value.gutters.length||!value.fixed)return null;let before=value.dom.offsetWidth*view.scaleX,after=value.domAfter?value.domAfter.offsetWidth*view.scaleX:0;return view.textDirection==exports.Direction.LTR?{left:before,right:after}:{right:before,left:after}})});/**
|
|
1206
|
+
Facet used to add a class to all gutter elements next to a widget.
|
|
1207
|
+
Should not provide widgets with a `toDOM` method.
|
|
1208
|
+
*/class UpdateContext{constructor(gutter,viewport,height){this.gutter=gutter,this.height=height,this.i=0,this.cursor=state.RangeSet.iter(gutter.markers,viewport.from)}addElement(view,block,markers){let{gutter}=this,above=(block.top-this.height)/view.scaleY,height=block.height/view.scaleY;if(this.i==gutter.elements.length){let newElt=new GutterElement(view,height,above,markers);gutter.elements.push(newElt),gutter.dom.appendChild(newElt.dom)}else gutter.elements[this.i].update(view,height,above,markers);this.height=block.bottom,this.i++}line(view,line,extraMarkers){let localMarkers=[];advanceCursor(this.cursor,localMarkers,line.from),extraMarkers.length&&(localMarkers=localMarkers.concat(extraMarkers));let forLine=this.gutter.config.lineMarker(view,line,localMarkers);forLine&&localMarkers.unshift(forLine);let gutter=this.gutter;(0!=localMarkers.length||gutter.config.renderEmptyElements)&&this.addElement(view,line,localMarkers)}widget(view,block){let marker=this.gutter.config.widgetMarker(view,block.widget,block),markers=marker?[marker]:null;for(let cls of view.state.facet(gutterWidgetClass)){let marker=cls(view,block.widget,block);marker&&(markers||(markers=[])).push(marker)}markers&&this.addElement(view,block,markers)}finish(){for(let gutter=this.gutter,last;gutter.elements.length>this.i;)last=gutter.elements.pop(),gutter.dom.removeChild(last.dom),last.destroy()}}class SingleGutterView{constructor(view,config){for(let prop in this.view=view,this.config=config,this.elements=[],this.spacer=null,this.dom=document.createElement("div"),this.dom.className="cm-gutter"+(this.config.class?" "+this.config.class:""),config.domEventHandlers)this.dom.addEventListener(prop,event=>{let target=event.target,y;if(target!=this.dom&&this.dom.contains(target)){for(;target.parentNode!=this.dom;)target=target.parentNode;let rect=target.getBoundingClientRect();y=(rect.top+rect.bottom)/2}else y=event.clientY;let line=view.lineBlockAtHeight(y-view.documentTop);config.domEventHandlers[prop](view,line,event)&&event.preventDefault()});this.markers=asArray(config.markers(view)),config.initialSpacer&&(this.spacer=new GutterElement(view,0,0,[config.initialSpacer(view)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+="visibility: hidden; pointer-events: none")}update(update){let prevMarkers=this.markers;if(this.markers=asArray(this.config.markers(update.view)),this.spacer&&this.config.updateSpacer){let updated=this.config.updateSpacer(this.spacer.markers[0],update);updated!=this.spacer.markers[0]&&this.spacer.update(update.view,0,0,[updated])}let vp=update.view.viewport;return!state.RangeSet.eq(this.markers,prevMarkers,vp.from,vp.to)||!!this.config.lineMarkerChange&&this.config.lineMarkerChange(update)}destroy(){for(let elt of this.elements)elt.destroy()}}class GutterElement{constructor(view,height,above,markers){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement("div"),this.dom.className="cm-gutterElement",this.update(view,height,above,markers)}update(view,height,above,markers){this.height!=height&&(this.height=height,this.dom.style.height=height+"px"),this.above!=above&&(this.dom.style.marginTop=(this.above=above)?above+"px":""),sameMarkers(this.markers,markers)||this.setMarkers(view,markers)}setMarkers(view,markers){let cls="cm-gutterElement",domPos=this.dom.firstChild;for(let iNew=0,iOld=0;;){let skipTo=iOld,marker=iNew<markers.length?markers[iNew++]:null,matched=!1;if(marker){let c=marker.elementClass;c&&(cls+=" "+c);for(let i=iOld;i<this.markers.length;i++)if(this.markers[i].compare(marker)){skipTo=i,matched=!0;break}}else skipTo=this.markers.length;for(;iOld<skipTo;){let next=this.markers[iOld++];if(next.toDOM){next.destroy(domPos);let after=domPos.nextSibling;domPos.remove(),domPos=after}}if(!marker)break;marker.toDOM&&(matched?domPos=domPos.nextSibling:this.dom.insertBefore(marker.toDOM(view),domPos)),matched&&iOld++}this.dom.className=cls,this.markers=markers}destroy(){this.setMarkers(null,[])}}const lineNumberMarkers=state.Facet.define(),lineNumberWidgetMarker=state.Facet.define(),lineNumberConfig=state.Facet.define({combine(values){return state.combineConfig(values,{formatNumber:String,domEventHandlers:{}},{domEventHandlers(a,b){let result=Object.assign({},a);for(let event in b){let exists=result[event],add=b[event];result[event]=exists?(view,line,event)=>exists(view,line,event)||add(view,line,event):add}return result}})}});/**
|
|
1209
|
+
Facet used to create markers in the line number gutter next to widgets.
|
|
1210
|
+
*/class NumberMarker extends GutterMarker{constructor(number){super(),this.number=number}eq(other){return this.number==other.number}toDOM(){return document.createTextNode(this.number)}}const lineNumberGutter=activeGutters.compute([lineNumberConfig],state=>({class:"cm-lineNumbers",renderEmptyElements:!1,markers(view){return view.state.facet(lineNumberMarkers)},lineMarker(view,line,others){return others.some(m=>m.toDOM)?null:new NumberMarker(formatNumber(view,view.state.doc.lineAt(line.from).number))},widgetMarker:(view,widget,block)=>{for(let m of view.state.facet(lineNumberWidgetMarker)){let result=m(view,widget,block);if(result)return result}return null},lineMarkerChange:update=>update.startState.facet(lineNumberConfig)!=update.state.facet(lineNumberConfig),initialSpacer(view){return new NumberMarker(formatNumber(view,maxLineNumber(view.state.doc.lines)))},updateSpacer(spacer,update){let max=formatNumber(update.view,maxLineNumber(update.view.state.doc.lines));return max==spacer.number?spacer:new NumberMarker(max)},domEventHandlers:state.facet(lineNumberConfig).domEventHandlers,side:"before"})),activeLineGutterMarker=new class extends GutterMarker{constructor(){super(...arguments),this.elementClass="cm-activeLineGutter"}},activeLineGutterHighlighter=gutterLineClass.compute(["selection"],state$1=>{let marks=[],last=-1;for(let range of state$1.selection.ranges){let linePos=state$1.doc.lineAt(range.head).from;linePos>last&&(last=linePos,marks.push(activeLineGutterMarker.range(linePos)))}return state.RangeSet.of(marks)}),tabDeco=Decoration.mark({class:"cm-highlightTab"}),spaceDeco=Decoration.mark({class:"cm-highlightSpace"}),whitespaceHighlighter=matcher(new MatchDecorator({regexp:/\t| /g,decoration:match=>"\t"==match[0]?tabDeco:spaceDeco,boundary:/\S/})),trailingHighlighter=matcher(new MatchDecorator({regexp:/\s+$/g,decoration:Decoration.mark({class:"cm-trailingSpace"})})),__test={HeightMap,HeightOracle,MeasuredHeights,QueryType,ChangedRange,computeOrder,moveVisually,clearHeightChangeFlag,getHeightChangeFlag:()=>heightChangeFlag};exports.BidiSpan=BidiSpan,exports.BlockInfo=BlockInfo,exports.BlockWrapper=BlockWrapper,exports.Decoration=Decoration,exports.EditorView=EditorView,exports.GutterMarker=GutterMarker,exports.MatchDecorator=MatchDecorator,exports.RectangleMarker=RectangleMarker,exports.ViewPlugin=ViewPlugin,exports.ViewUpdate=ViewUpdate,exports.WidgetType=WidgetType,exports.__test=__test,exports.activateHover=activateHover,exports.closeHoverTooltip=closeHoverTooltip,exports.closeHoverTooltips=closeHoverTooltips,exports.crosshairCursor=crosshairCursor,exports.drawSelection=drawSelection,exports.dropCursor=dropCursor,exports.getDialog=getDialog,exports.getDrawSelectionConfig=getDrawSelectionConfig,exports.getPanel=getPanel,exports.getTooltip=getTooltip,exports.gutter=gutter,exports.gutterLineClass=gutterLineClass,exports.gutterWidgetClass=gutterWidgetClass,exports.gutters=gutters,exports.hasHoverTooltips=hasHoverTooltips,exports.highlightActiveLine=highlightActiveLine,exports.highlightActiveLineGutter=highlightActiveLineGutter,exports.highlightSpecialChars=highlightSpecialChars,exports.highlightTrailingWhitespace=highlightTrailingWhitespace,exports.highlightWhitespace=highlightWhitespace,exports.hoverTooltip=hoverTooltip,exports.keymap=keymap,exports.layer=layer,exports.lineNumberMarkers=lineNumberMarkers,exports.lineNumberWidgetMarker=lineNumberWidgetMarker,exports.lineNumbers=lineNumbers,exports.logException=logException,exports.panels=panels,exports.placeholder=placeholder,exports.rectangularSelection=rectangularSelection,exports.repositionTooltips=repositionTooltips,exports.runScopeHandlers=runScopeHandlers,exports.scrollPastEnd=scrollPastEnd,exports.showDialog=showDialog,exports.showPanel=showPanel,exports.showTooltip=showTooltip,exports.tooltips=tooltips}(dist),dist)}(),index=/*@__PURE__*/function getDefaultExportFromCjs(x){return x}(distExports),hasRequiredDist;return module.exports=index,module.exports}
|