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

  1. import{Annotation,EditorSelection,codePointAt,codePointSize,fromCodePoint,Facet,combineConfig,StateEffect,StateField,Prec,Text,MapMode,RangeValue,RangeSet,CharCategory}from"@codemirror/state";import{logException,Direction,showTooltip,EditorView,ViewPlugin,getTooltip,Decoration,WidgetType,keymap}from"@codemirror/view";import{syntaxTree,indentUnit}from"@codemirror/language";class CompletionContext{constructor(state,pos,explicit){this.state=state;this.pos=pos;this.explicit=explicit;this.abortListeners=[]}tokenBefore(types){let token=syntaxTree(this.state).resolveInner(this.pos,-1);while(token&&types.indexOf(token.name)<0)token=token.parent;return token?{from:token.from,to:this.pos,text:this.state.sliceDoc(token.from,this.pos),type:token.type}:null}matchBefore(expr){let line=this.state.doc.lineAt(this.pos);let start=Math.max(line.from,this.pos-250);let str=line.text.slice(start-line.from,this.pos-line.from);let found=str.search(ensureAnchor(expr,false));return found<0?null:{from:start+found,to:this.pos,text:str.slice(found)}}get aborted(){return this.abortListeners==null}addEventListener(type,listener){if(type=="abort"&&this.abortListeners)this.abortListeners.push(listener)}}function toSet(chars){let flat=Object.keys(chars).join("");let words=/\w/.test(flat);if(words)flat=flat.replace(/\w/g,"");return`[${words?"\\w":""}${flat.replace(/[^\w\s]/g,"\\$&")}]`}function prefixMatch(options){let first=Object.create(null),rest=Object.create(null);for(let{label}of options){first[label[0]]=true;for(let i=1;i<label.length;i++)rest[label[i]]=true}let source=toSet(first)+toSet(rest)+"*$";return[new RegExp("^"+source),new RegExp(source)]}function completeFromList(list){let options=list.map(o=>typeof o=="string"?{label:o}:o);let[validFor,match]=options.every(o=>/^\w+$/.test(o.label))?[/\w*$/,/\w+$/]:prefixMatch(options);return context=>{let token=context.matchBefore(match);return token||context.explicit?{from:token?token.from:context.pos,options:options,validFor:validFor}:null}}function ifIn(nodes,source){return context=>{for(let pos=syntaxTree(context.state).resolveInner(context.pos,-1);pos;pos=pos.parent)if(nodes.indexOf(pos.name)>-1)return source(context);return null}}function ifNotIn(nodes,source){return context=>{for(let pos=syntaxTree(context.state).resolveInner(context.pos,-1);pos;pos=pos.parent)if(nodes.indexOf(pos.name)>-1)return null;return source(context)}}class Option{constructor(completion,source,match){this.completion=completion;this.source=source;this.match=match}}function cur(state){return state.selection.main.head}function ensureAnchor(expr,start){var _a;let{source}=expr;let addStart=start&&source[0]!="^",addEnd=source[source.length-1]!="$";if(!addStart&&!addEnd)return expr;return new RegExp(`${addStart?"^":""}(?:${source})${addEnd?"$":""}`,(_a=expr.flags)!==null&&_a!==void 0?_a:expr.ignoreCase?"i":"")}const pickedCompletion=Annotation.define();function insertCompletionText(state,text,from,to){return Object.assign(Object.assign({},state.changeByRange(range=>{if(range==state.selection.main)return{changes:{from:from,to:to,insert:text},range:EditorSelection.cursor(from+text.length)};let len=to-from;if(!range.empty||len&&state.sliceDoc(range.from-len,range.from)!=state.sliceDoc(from,to))return{range:range};return{changes:{from:range.from-len,to:range.from,insert:text},range:EditorSelection.cursor(range.from-len+text.length)}})),{userEvent:"input.complete"})}function applyCompletion(view,option){const apply=option.completion.apply||option.completion.label;let result=option.source;if(typeof apply=="string")view.dispatch(insertCompletionText(view.state,apply,result.from,result.to));else apply(view,option.completion,result.from,result.to)}const SourceCache=new WeakMap;function asSource(source){if(!Array.isArray(source))return source;let known=SourceCache.get(source);if(!known)SourceCache.set(source,known=completeFromList(source));return known}class FuzzyMatcher{constructor(pattern){this.pattern=pattern;this.chars=[];this.folded=[];this.any=[];this.precise=[];this.byWord=[];for(let p=0;p<pattern.length;){let char=codePointAt(patte