A Wikiful of Hacks: Hacks.Wiki is an experiment to organise quick hacks, notes, bookmarks and tools into an easy-to-build-and-maintain “Digital Garden”.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

0 lines
8.9 KiB

  1. import{EditorSelection,Prec}from"@codemirror/state";import{keymap}from"@codemirror/view";import{defineLanguageFacet,foldNodeProp,indentNodeProp,languageDataProp,Language,LanguageDescription,ParseContext,syntaxTree,LanguageSupport}from"@codemirror/language";import{parser,GFM,Subscript,Superscript,Emoji,MarkdownParser,parseCode}from"@lezer/markdown";import{html}from"@codemirror/lang-html";const data=defineLanguageFacet({block:{open:"\x3c!--",close:"--\x3e"}});const commonmark=parser.configure({props:[foldNodeProp.add(type=>{if(!type.is("Block")||type.is("Document"))return undefined;return(tree,state)=>({from:state.doc.lineAt(tree.from).to,to:tree.to})}),indentNodeProp.add({Document:()=>null}),languageDataProp.add({Document:data})]});function mkLang(parser){return new Language(data,parser,[],"markdown")}const commonmarkLanguage=mkLang(commonmark);const extended=commonmark.configure([GFM,Subscript,Superscript,Emoji]);const markdownLanguage=mkLang(extended);function getCodeParser(languages,defaultLanguage){return info=>{if(info&&languages){let found=null;info=/\S*/.exec(info)[0];if(typeof languages=="function")found=languages(info);else found=LanguageDescription.matchLanguageName(languages,info,true);if(found instanceof LanguageDescription)return found.support?found.support.language.parser:ParseContext.getSkippingParser(found.load());else if(found)return found.parser}return defaultLanguage?defaultLanguage.parser:null}}class Context{constructor(node,from,to,spaceBefore,spaceAfter,type,item){this.node=node;this.from=from;this.to=to;this.spaceBefore=spaceBefore;this.spaceAfter=spaceAfter;this.type=type;this.item=item}blank(maxWidth,trailing=true){let result=this.spaceBefore+(this.node.name=="Blockquote"?">":"");if(maxWidth!=null){while(result.length<maxWidth)result+=" ";return result}else{for(let i=this.to-this.from-result.length-this.spaceAfter.length;i>0;i--)result+=" ";return result+(trailing?this.spaceAfter:"")}}marker(doc,add){let number=this.node.name=="OrderedList"?String(+itemNumber(this.item,doc)[2]+add):"";return this.spaceBefore+number+this.type+this.spaceAfter}}function getContext(node,doc){let nodes=[];for(let cur=node;cur&&cur.name!="Document";cur=cur.parent){if(cur.name=="ListItem"||cur.name=="Blockquote"||cur.name=="FencedCode")nodes.push(cur)}let context=[];for(let i=nodes.length-1;i>=0;i--){let node=nodes[i],match;let line=doc.lineAt(node.from),startPos=node.from-line.from;if(node.name=="FencedCode"){context.push(new Context(node,startPos,startPos,"","","",null))}else if(node.name=="Blockquote"&&(match=/^[ \t]*>( ?)/.exec(line.text.slice(startPos)))){context.push(new Context(node,startPos,startPos+match[0].length,"",match[1],">",null))}else if(node.name=="ListItem"&&node.parent.name=="OrderedList"&&(match=/^([ \t]*)\d+([.)])([ \t]*)/.exec(line.text.slice(startPos)))){let after=match[3],len=match[0].length;if(after.length>=4){after=after.slice(0,after.length-4);len-=4}context.push(new Context(node.parent,startPos,startPos+len,match[1],after,match[2],node))}else if(node.name=="ListItem"&&node.parent.name=="BulletList"&&(match=/^([ \t]*)([-+*])([ \t]{1,4}\[[ xX]\])?([ \t]+)/.exec(line.text.slice(startPos)))){let after=match[4],len=match[0].length;if(after.length>4){after=after.slice(0,after.length-4);len-=4}let type=match[2];if(match[3])type+=match[3].replace(/[xX]/," ");context.push(new Context(node.parent,startPos,startPos+len,match[1],after,type,node))}}return context}function itemNumber(item,doc){return/^(\s*)(\d+)(?=[.)])/.exec(doc.sliceString(item.from,item.from+10))}function renumberList(after,doc,changes,offset=0){for(let prev=-1,node=after;;){if(node.name=="ListItem"){let m=itemNumber(node,doc);let number=+m[2];if(prev>=0){if(number!=prev+1)return;changes.push({from:node.from+m[1].length,to:node.from+m[0].length,insert:String(prev+2+offset)})}prev=number}let next=node.nextSibling;if(!next)break;node=next}}const insertNewlineContinueMarkup=({state,dispatch})=>{let tree=syntaxTree(state),{doc}=state;let dont=null,changes=state.changeByRange(range=>{if(!range.empty||!markdownLanguage.isActiveAt(state,range.