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
28 KiB

  1. import{showPanel,EditorView,getPanel,Decoration,ViewPlugin,runScopeHandlers}from"@codemirror/view";import{codePointAt,fromCodePoint,codePointSize,StateEffect,StateField,EditorSelection,Facet,combineConfig,CharCategory,RangeSetBuilder,Prec,EditorState,findClusterBreak}from"@codemirror/state";import elt from"crelt";const basicNormalize=typeof String.prototype.normalize=="function"?x=>x.normalize("NFKD"):x=>x;class SearchCursor{constructor(text,query,from=0,to=text.length,normalize,test){this.test=test;this.value={from:0,to:0};this.done=false;this.matches=[];this.buffer="";this.bufferPos=0;this.iter=text.iterRange(from,to);this.bufferStart=from;this.normalize=normalize?x=>normalize(basicNormalize(x)):basicNormalize;this.query=this.normalize(query)}peek(){if(this.bufferPos==this.buffer.length){this.bufferStart+=this.buffer.length;this.iter.next();if(this.iter.done)return-1;this.bufferPos=0;this.buffer=this.iter.value}return codePointAt(this.buffer,this.bufferPos)}next(){while(this.matches.length)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let next=this.peek();if(next<0){this.done=true;return this}let str=fromCodePoint(next),start=this.bufferStart+this.bufferPos;this.bufferPos+=codePointSize(next);let norm=this.normalize(str);for(let i=0,pos=start;;i++){let code=norm.charCodeAt(i);let match=this.match(code,pos);if(match){this.value=match;return this}if(i==norm.length-1)break;if(pos==start&&i<str.length&&str.charCodeAt(i)==code)pos++}}}match(code,pos){let match=null;for(let i=0;i<this.matches.length;i+=2){let index=this.matches[i],keep=false;if(this.query.charCodeAt(index)==code){if(index==this.query.length-1){match={from:this.matches[i+1],to:pos+1}}else{this.matches[i]++;keep=true}}if(!keep){this.matches.splice(i,2);i-=2}}if(this.query.charCodeAt(0)==code){if(this.query.length==1)match={from:pos,to:pos+1};else this.matches.push(1,pos)}if(match&&this.test&&!this.test(match.from,match.to,this.buffer,this.bufferPos))match=null;return match}}if(typeof Symbol!="undefined")SearchCursor.prototype[Symbol.iterator]=function(){return this};const empty={from:-1,to:-1,match:/.*/.exec("")};const baseFlags="gm"+(/x/.unicode==null?"":"u");class RegExpCursor{constructor(text,query,options,from=0,to=text.length){this.text=text;this.to=to;this.curLine="";this.done=false;this.value=empty;if(/\\[sWDnr]|\n|\r|\[\^/.test(query))return new MultilineRegExpCursor(text,query,options,from,to);this.re=new RegExp(query,baseFlags+((options===null||options===void 0?void 0:options.ignoreCase)?"i":""));this.test=options===null||options===void 0?void 0:options.test;this.iter=text.iter();let startLine=text.lineAt(from);this.curLineStart=startLine.from;this.matchPos=toCharEnd(text,from);this.getLine(this.curLineStart)}getLine(skip){this.iter.next(skip);if(this.iter.lineBreak){this.curLine=""}else{this.curLine=this.iter.value;if(this.curLineStart+this.curLine.length>this.to)this.curLine=this.curLine.slice(0,this.to-this.curLineStart);this.iter.next()}}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1;if(this.curLineStart>this.to)this.curLine="";else this.getLine(0)}next(){for(let off=this.matchPos-this.curLineStart;;){this.re.lastIndex=off;let match=this.matchPos<=this.to&&this.re.exec(this.curLine);if(match){let from=this.curLineStart+match.index,to=from+match[0].length;this.matchPos=toCharEnd(this.text,to+(from==to?1:0));if(from==this.curLineStart+this.curLine.length)this.nextLine();if((from<to||from>this.value.to)&&(!this.test||this.test(from,to,match))){this.value={from:from,to:to,match:match};return this}off=this.matchPos-this.curLineStart}else if(this.curLineStart+this.curLine.length<this.to){this.nextLine();off=0}else{this.done=true;return this}}}}const flattened=new WeakMap;class FlattenedDoc{constructor(from,text){this.from=from;this.text=text}get to(){return this.from+this.text.length}static get(doc,from,to){let cached=flattened.get(doc);if(!cached||cached.from>=to||cached.to<=from){let flat=new FlattenedDoc(from,doc.sliceString(from,to));flattened.set(doc,flat);return flat}if(cached.from==from&&cached