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.
 
 

1 lines
39 KiB

import{Annotation,Facet,combineConfig,StateField,EditorSelection,Transaction,ChangeSet,ChangeDesc,StateEffect,Text,findClusterBreak,countColumn,CharCategory}from"@codemirror/state";import{EditorView,Direction}from"@codemirror/view";import{IndentContext,getIndentation,indentString,indentUnit,getIndentUnit,matchBrackets,syntaxTree}from"@codemirror/language";import{NodeProp}from"@lezer/common";const toggleComment=target=>{let config=getConfig(target.state);return config.line?toggleLineComment(target):config.block?toggleBlockCommentByLine(target):false};function command(f,option){return({state,dispatch})=>{if(state.readOnly)return false;let tr=f(option,state);if(!tr)return false;dispatch(state.update(tr));return true}}const toggleLineComment=command(changeLineComment,0);const lineComment=command(changeLineComment,1);const lineUncomment=command(changeLineComment,2);const toggleBlockComment=command(changeBlockComment,0);const blockComment=command(changeBlockComment,1);const blockUncomment=command(changeBlockComment,2);const toggleBlockCommentByLine=command((o,s)=>changeBlockComment(o,s,selectedLineRanges(s)),0);function getConfig(state,pos=state.selection.main.head){let data=state.languageDataAt("commentTokens",pos);return data.length?data[0]:{}}const SearchMargin=50;function findBlockComment(state,{open,close},from,to){let textBefore=state.sliceDoc(from-SearchMargin,from);let textAfter=state.sliceDoc(to,to+SearchMargin);let spaceBefore=/\s*$/.exec(textBefore)[0].length,spaceAfter=/^\s*/.exec(textAfter)[0].length;let beforeOff=textBefore.length-spaceBefore;if(textBefore.slice(beforeOff-open.length,beforeOff)==open&&textAfter.slice(spaceAfter,spaceAfter+close.length)==close){return{open:{pos:from-spaceBefore,margin:spaceBefore&&1},close:{pos:to+spaceAfter,margin:spaceAfter&&1}}}let startText,endText;if(to-from<=2*SearchMargin){startText=endText=state.sliceDoc(from,to)}else{startText=state.sliceDoc(from,from+SearchMargin);endText=state.sliceDoc(to-SearchMargin,to)}let startSpace=/^\s*/.exec(startText)[0].length,endSpace=/\s*$/.exec(endText)[0].length;let endOff=endText.length-endSpace-close.length;if(startText.slice(startSpace,startSpace+open.length)==open&&endText.slice(endOff,endOff+close.length)==close){return{open:{pos:from+startSpace+open.length,margin:/\s/.test(startText.charAt(startSpace+open.length))?1:0},close:{pos:to-endSpace-close.length,margin:/\s/.test(endText.charAt(endOff-1))?1:0}}}return null}function selectedLineRanges(state){let ranges=[];for(let r of state.selection.ranges){let fromLine=state.doc.lineAt(r.from);let toLine=r.to<=fromLine.to?fromLine:state.doc.lineAt(r.to);let last=ranges.length-1;if(last>=0&&ranges[last].to>fromLine.from)ranges[last].to=toLine.to;else ranges.push({from:fromLine.from,to:toLine.to})}return ranges}function changeBlockComment(option,state,ranges=state.selection.ranges){let tokens=ranges.map(r=>getConfig(state,r.from).block);if(!tokens.every(c=>c))return null;let comments=ranges.map((r,i)=>findBlockComment(state,tokens[i],r.from,r.to));if(option!=2&&!comments.every(c=>c)){return{changes:state.changes(ranges.map((range,i)=>{if(comments[i])return[];return[{from:range.from,insert:tokens[i].open+" "},{from:range.to,insert:" "+tokens[i].close}]}))}}else if(option!=1&&comments.some(c=>c)){let changes=[];for(let i=0,comment;i<comments.length;i++)if(comment=comments[i]){let token=tokens[i],{open,close}=comment;changes.push({from:open.pos-token.open.length,to:open.pos+open.margin},{from:close.pos-close.margin,to:close.pos+token.close.length})}return{changes:changes}}return null}function changeLineComment(option,state,ranges=state.selection.ranges){let lines=[];let prevLine=-1;for(let{from,to}of ranges){let startI=lines.length,minIndent=1e9;for(let pos=from;pos<=to;){let line=state.doc.lineAt(pos);if(line.from>prevLine&&(from==to||to>line.from)){prevLine=line.from;let token=getConfig(state,pos).line;if(!token)continue;let indent=/^\s*/.exec(line.text)[0].length;let empty=indent==line.length;let comment=line.text.slice(indent,indent+token.length)==token?indent:-1;if(indent<line.text.length&&indent<minIndent)minIndent=indent;lines.push({line:line,comment:comment,token:token,indent:indent,empty:empty,single:false})}pos=line.to+1}if(minIndent<1e9)for(let i=startI;i<lines.length;i++)if(lines[i].indent<lines[i].line.text.length)lines[i].indent=minIndent;if(lines.length==startI+1)lines[startI].single=true}if(option!=2&&lines.some(l=>l.comment<0&&(!l.empty||l.single))){let changes=[];for(let{line,token,indent,empty,single}of lines)if(single||!empty)changes.push({from:line.from+indent,insert:token+" "});let changeSet=state.changes(changes);return{changes:changeSet,selection:state.selection.map(changeSet,1)}}else if(option!=1&&lines.some(l=>l.comment>=0)){let changes=[];for(let{line,comment,token}of lines)if(comment>=0){let from=line.from+comment,to=from+token.length;if(line.text[to-line.from]==" ")to++;changes.push({from:from,to:to})}return{changes:changes}}return null}const fromHistory=Annotation.define();const isolateHistory=Annotation.define();const invertedEffects=Facet.define();const historyConfig=Facet.define({combine(configs){return combineConfig(configs,{minDepth:100,newGroupDelay:500},{minDepth:Math.max,newGroupDelay:Math.min})}});function changeEnd(changes){let end=0;changes.iterChangedRanges((_,to)=>end=to);return end}const historyField_=StateField.define({create(){return HistoryState.empty},update(state,tr){let config=tr.state.facet(historyConfig);let fromHist=tr.annotation(fromHistory);if(fromHist){let selection=tr.docChanged?EditorSelection.single(changeEnd(tr.changes)):undefined;let item=HistEvent.fromTransaction(tr,selection),from=fromHist.side;let other=from==0?state.undone:state.done;if(item)other=updateBranch(other,other.length,config.minDepth,item);else other=addSelection(other,tr.startState.selection);return new HistoryState(from==0?fromHist.rest:other,from==0?other:fromHist.rest)}let isolate=tr.annotation(isolateHistory);if(isolate=="full"||isolate=="before")state=state.isolate();if(tr.annotation(Transaction.addToHistory)===false)return!tr.changes.empty?state.addMapping(tr.changes.desc):state;let event=HistEvent.fromTransaction(tr);let time=tr.annotation(Transaction.time),userEvent=tr.annotation(Transaction.userEvent);if(event)state=state.addChanges(event,time,userEvent,config.newGroupDelay,config.minDepth);else if(tr.selection)state=state.addSelection(tr.startState.selection,time,userEvent,config.newGroupDelay);if(isolate=="full"||isolate=="after")state=state.isolate();return state},toJSON(value){return{done:value.done.map(e=>e.toJSON()),undone:value.undone.map(e=>e.toJSON())}},fromJSON(json){return new HistoryState(json.done.map(HistEvent.fromJSON),json.undone.map(HistEvent.fromJSON))}});function history(config={}){return[historyField_,historyConfig.of(config),EditorView.domEventHandlers({beforeinput(e,view){let command=e.inputType=="historyUndo"?undo:e.inputType=="historyRedo"?redo:null;if(!command)return false;e.preventDefault();return command(view)}})]}const historyField=historyField_;function cmd(side,selection){return function({state,dispatch}){if(!selection&&state.readOnly)return false;let historyState=state.field(historyField_,false);if(!historyState)return false;let tr=historyState.pop(side,state,selection);if(!tr)return false;dispatch(tr);return true}}const undo=cmd(0,false);const redo=cmd(1,false);const undoSelection=cmd(0,true);const redoSelection=cmd(1,true);function depth(side){return function(state){let histState=state.field(historyField_,false);if(!histState)return 0;let branch=side==0?histState.done:histState.undone;return branch.length-(branch.length&&!branch[0].changes?1:0)}}const undoDepth=depth(0);const redoDepth=depth(1);class HistEvent{constructor(changes,effects,mapped,startSelection,selectionsAfter){this.changes=changes;this.effects=effects;this.mapped=mapped;this.startSelection=startSelection;this.selectionsAfter=selectionsAfter}setSelAfter(after){return new HistEvent(this.changes,this.effects,this.mapped,this.startSelection,after)}toJSON(){var _a,_b,_c;return{changes:(_a=this.changes)===null||_a===void 0?void 0:_a.toJSON(),mapped:(_b=this.mapped)===null||_b===void 0?void 0:_b.toJSON(),startSelection:(_c=this.startSelection)===null||_c===void 0?void 0:_c.toJSON(),selectionsAfter:this.selectionsAfter.map(s=>s.toJSON())}}static fromJSON(json){return new HistEvent(json.changes&&ChangeSet.fromJSON(json.changes),[],json.mapped&&ChangeDesc.fromJSON(json.mapped),json.startSelection&&EditorSelection.fromJSON(json.startSelection),json.selectionsAfter.map(EditorSelection.fromJSON))}static fromTransaction(tr,selection){let effects=none;for(let invert of tr.startState.facet(invertedEffects)){let result=invert(tr);if(result.length)effects=effects.concat(result)}if(!effects.length&&tr.changes.empty)return null;return new HistEvent(tr.changes.invert(tr.startState.doc),effects,undefined,selection||tr.startState.selection,none)}static selection(selections){return new HistEvent(undefined,none,undefined,undefined,selections)}}function updateBranch(branch,to,maxLen,newEvent){let start=to+1>maxLen+20?to-maxLen-1:0;let newBranch=branch.slice(start,to);newBranch.push(newEvent);return newBranch}function isAdjacent(a,b){let ranges=[],isAdjacent=false;a.iterChangedRanges((f,t)=>ranges.push(f,t));b.iterChangedRanges((_f,_t,f,t)=>{for(let i=0;i<ranges.length;){let from=ranges[i++],to=ranges[i++];if(t>=from&&f<=to)isAdjacent=true}});return isAdjacent}function eqSelectionShape(a,b){return a.ranges.length==b.ranges.length&&a.ranges.filter((r,i)=>r.empty!=b.ranges[i].empty).length===0}function conc(a,b){return!a.length?b:!b.length?a:a.concat(b)}const none=[];const MaxSelectionsPerEvent=200;function addSelection(branch,selection){if(!branch.length){return[HistEvent.selection([selection])]}else{let lastEvent=branch[branch.length-1];let sels=lastEvent.selectionsAfter.slice(Math.max(0,lastEvent.selectionsAfter.length-MaxSelectionsPerEvent));if(sels.length&&sels[sels.length-1].eq(selection))return branch;sels.push(selection);return updateBranch(branch,branch.length-1,1e9,lastEvent.setSelAfter(sels))}}function popSelection(branch){let last=branch[branch.length-1];let newBranch=branch.slice();newBranch[branch.length-1]=last.setSelAfter(last.selectionsAfter.slice(0,last.selectionsAfter.length-1));return newBranch}function addMappingToBranch(branch,mapping){if(!branch.length)return branch;let length=branch.length,selections=none;while(length){let event=mapEvent(branch[length-1],mapping,selections);if(event.changes&&!event.changes.empty||event.effects.length){let result=branch.slice(0,length);result[length-1]=event;return result}else{mapping=event.mapped;length--;selections=event.selectionsAfter}}return selections.length?[HistEvent.selection(selections)]:none}function mapEvent(event,mapping,extraSelections){let selections=conc(event.selectionsAfter.length?event.selectionsAfter.map(s=>s.map(mapping)):none,extraSelections);if(!event.changes)return HistEvent.selection(selections);let mappedChanges=event.changes.map(mapping),before=mapping.mapDesc(event.changes,true);let fullMapping=event.mapped?event.mapped.composeDesc(before):before;return new HistEvent(mappedChanges,StateEffect.mapEffects(event.effects,mapping),fullMapping,event.startSelection.map(before),selections)}const joinableUserEvent=/^(input\.type|delete)($|\.)/;class HistoryState{constructor(done,undone,prevTime=0,prevUserEvent=undefined){this.done=done;this.undone=undone;this.prevTime=prevTime;this.prevUserEvent=prevUserEvent}isolate(){return this.prevTime?new HistoryState(this.done,this.undone):this}addChanges(event,time,userEvent,newGroupDelay,maxLen){let done=this.done,lastEvent=done[done.length-1];if(lastEvent&&lastEvent.changes&&!lastEvent.changes.empty&&event.changes&&(!userEvent||joinableUserEvent.test(userEvent))&&(!lastEvent.selectionsAfter.length&&time-this.prevTime<newGroupDelay&&isAdjacent(lastEvent.changes,event.changes)||userEvent=="input.type.compose")){done=updateBranch(done,done.length-1,maxLen,new HistEvent(event.changes.compose(lastEvent.changes),conc(event.effects,lastEvent.effects),lastEvent.mapped,lastEvent.startSelection,none))}else{done=updateBranch(done,done.length,maxLen,event)}return new HistoryState(done,none,time,userEvent)}addSelection(selection,time,userEvent,newGroupDelay){let last=this.done.length?this.done[this.done.length-1].selectionsAfter:none;if(last.length>0&&time-this.prevTime<newGroupDelay&&userEvent==this.prevUserEvent&&userEvent&&/^select($|\.)/.test(userEvent)&&eqSelectionShape(last[last.length-1],selection))return this;return new HistoryState(addSelection(this.done,selection),this.undone,time,userEvent)}addMapping(mapping){return new HistoryState(addMappingToBranch(this.done,mapping),addMappingToBranch(this.undone,mapping),this.prevTime,this.prevUserEvent)}pop(side,state,selection){let branch=side==0?this.done:this.undone;if(branch.length==0)return null;let event=branch[branch.length-1];if(selection&&event.selectionsAfter.length){return state.update({selection:event.selectionsAfter[event.selectionsAfter.length-1],annotations:fromHistory.of({side:side,rest:popSelection(branch)}),userEvent:side==0?"select.undo":"select.redo",scrollIntoView:true})}else if(!event.changes){return null}else{let rest=branch.length==1?none:branch.slice(0,branch.length-1);if(event.mapped)rest=addMappingToBranch(rest,event.mapped);return state.update({changes:event.changes,selection:event.startSelection,effects:event.effects,annotations:fromHistory.of({side:side,rest:rest}),filter:false,userEvent:side==0?"undo":"redo",scrollIntoView:true})}}}HistoryState.empty=new HistoryState(none,none);const historyKeymap=[{key:"Mod-z",run:undo,preventDefault:true},{key:"Mod-y",mac:"Mod-Shift-z",run:redo,preventDefault:true},{linux:"Ctrl-Shift-z",run:redo,preventDefault:true},{key:"Mod-u",run:undoSelection,preventDefault:true},{key:"Alt-u",mac:"Mod-Shift-u",run:redoSelection,preventDefault:true}];function updateSel(sel,by){return EditorSelection.create(sel.ranges.map(by),sel.mainIndex)}function setSel(state,selection){return state.update({selection:selection,scrollIntoView:true,userEvent:"select"})}function moveSel({state,dispatch},how){let selection=updateSel(state.selection,how);if(selection.eq(state.selection))return false;dispatch(setSel(state,selection));return true}function rangeEnd(range,forward){return EditorSelection.cursor(forward?range.to:range.from)}function cursorByChar(view,forward){return moveSel(view,range=>range.empty?view.moveByChar(range,forward):rangeEnd(range,forward))}function ltrAtCursor(view){return view.textDirectionAt(view.state.selection.main.head)==Direction.LTR}const cursorCharLeft=view=>cursorByChar(view,!ltrAtCursor(view));const cursorCharRight=view=>cursorByChar(view,ltrAtCursor(view));const cursorCharForward=view=>cursorByChar(view,true);const cursorCharBackward=view=>cursorByChar(view,false);function cursorByGroup(view,forward){return moveSel(view,range=>range.empty?view.moveByGroup(range,forward):rangeEnd(range,forward))}const cursorGroupLeft=view=>cursorByGroup(view,!ltrAtCursor(view));const cursorGroupRight=view=>cursorByGroup(view,ltrAtCursor(view));const cursorGroupForward=view=>cursorByGroup(view,true);const cursorGroupBackward=view=>cursorByGroup(view,false);function moveBySubword(view,range,forward){let categorize=view.state.charCategorizer(range.from);return view.moveByChar(range,forward,start=>{let cat=CharCategory.Space,pos=range.from;let done=false,sawUpper=false,sawLower=false;let step=next=>{if(done)return false;pos+=forward?next.length:-next.length;let nextCat=categorize(next),ahead;if(cat==CharCategory.Space)cat=nextCat;if(cat!=nextCat)return false;if(cat==CharCategory.Word){if(next.toLowerCase()==next){if(!forward&&sawUpper)return false;sawLower=true}else if(sawLower){if(forward)return false;done=true}else{if(sawUpper&&forward&&categorize(ahead=view.state.sliceDoc(pos,pos+1))==CharCategory.Word&&ahead.toLowerCase()==ahead)return false;sawUpper=true}}return true};step(start);return step})}function cursorBySubword(view,forward){return moveSel(view,range=>range.empty?moveBySubword(view,range,forward):rangeEnd(range,forward))}const cursorSubwordForward=view=>cursorBySubword(view,true);const cursorSubwordBackward=view=>cursorBySubword(view,false);function interestingNode(state,node,bracketProp){if(node.type.prop(bracketProp))return true;let len=node.to-node.from;return len&&(len>2||/[^\s,.;:]/.test(state.sliceDoc(node.from,node.to)))||node.firstChild}function moveBySyntax(state,start,forward){let pos=syntaxTree(state).resolveInner(start.head);let bracketProp=forward?NodeProp.closedBy:NodeProp.openedBy;for(let at=start.head;;){let next=forward?pos.childAfter(at):pos.childBefore(at);if(!next)break;if(interestingNode(state,next,bracketProp))pos=next;else at=forward?next.to:next.from}let bracket=pos.type.prop(bracketProp),match,newPos;if(bracket&&(match=forward?matchBrackets(state,pos.from,1):matchBrackets(state,pos.to,-1))&&match.matched)newPos=forward?match.end.to:match.end.from;else newPos=forward?pos.to:pos.from;return EditorSelection.cursor(newPos,forward?-1:1)}const cursorSyntaxLeft=view=>moveSel(view,range=>moveBySyntax(view.state,range,!ltrAtCursor(view)));const cursorSyntaxRight=view=>moveSel(view,range=>moveBySyntax(view.state,range,ltrAtCursor(view)));function cursorByLine(view,forward){return moveSel(view,range=>{if(!range.empty)return rangeEnd(range,forward);let moved=view.moveVertically(range,forward);return moved.head!=range.head?moved:view.moveToLineBoundary(range,forward)})}const cursorLineUp=view=>cursorByLine(view,false);const cursorLineDown=view=>cursorByLine(view,true);function pageHeight(view){return Math.max(view.defaultLineHeight,Math.min(view.dom.clientHeight,innerHeight)-5)}function cursorByPage(view,forward){let{state}=view,selection=updateSel(state.selection,range=>{return range.empty?view.moveVertically(range,forward,pageHeight(view)):rangeEnd(range,forward)});if(selection.eq(state.selection))return false;let startPos=view.coordsAtPos(state.selection.main.head);let scrollRect=view.scrollDOM.getBoundingClientRect();let effect;if(startPos&&startPos.top>scrollRect.top&&startPos.bottom<scrollRect.bottom&&startPos.top-scrollRect.top<=view.scrollDOM.scrollHeight-view.scrollDOM.scrollTop-view.scrollDOM.clientHeight)effect=EditorView.scrollIntoView(selection.main.head,{y:"start",yMargin:startPos.top-scrollRect.top});view.dispatch(setSel(state,selection),{effects:effect});return true}const cursorPageUp=view=>cursorByPage(view,false);const cursorPageDown=view=>cursorByPage(view,true);function moveByLineBoundary(view,start,forward){let line=view.lineBlockAt(start.head),moved=view.moveToLineBoundary(start,forward);if(moved.head==start.head&&moved.head!=(forward?line.to:line.from))moved=view.moveToLineBoundary(start,forward,false);if(!forward&&moved.head==line.from&&line.length){let space=/^\s*/.exec(view.state.sliceDoc(line.from,Math.min(line.from+100,line.to)))[0].length;if(space&&start.head!=line.from+space)moved=EditorSelection.cursor(line.from+space)}return moved}const cursorLineBoundaryForward=view=>moveSel(view,range=>moveByLineBoundary(view,range,true));const cursorLineBoundaryBackward=view=>moveSel(view,range=>moveByLineBoundary(view,range,false));const cursorLineBoundaryLeft=view=>moveSel(view,range=>moveByLineBoundary(view,range,!ltrAtCursor(view)));const cursorLineBoundaryRight=view=>moveSel(view,range=>moveByLineBoundary(view,range,ltrAtCursor(view)));const cursorLineStart=view=>moveSel(view,range=>EditorSelection.cursor(view.lineBlockAt(range.head).from,1));const cursorLineEnd=view=>moveSel(view,range=>EditorSelection.cursor(view.lineBlockAt(range.head).to,-1));function toMatchingBracket(state,dispatch,extend){let found=false,selection=updateSel(state.selection,range=>{let matching=matchBrackets(state,range.head,-1)||matchBrackets(state,range.head,1)||range.head>0&&matchBrackets(state,range.head-1,1)||range.head<state.doc.length&&matchBrackets(state,range.head+1,-1);if(!matching||!matching.end)return range;found=true;let head=matching.start.from==range.head?matching.end.to:matching.end.from;return extend?EditorSelection.range(range.anchor,head):EditorSelection.cursor(head)});if(!found)return false;dispatch(setSel(state,selection));return true}const cursorMatchingBracket=({state,dispatch})=>toMatchingBracket(state,dispatch,false);const selectMatchingBracket=({state,dispatch})=>toMatchingBracket(state,dispatch,true);function extendSel(view,how){let selection=updateSel(view.state.selection,range=>{let head=how(range);return EditorSelection.range(range.anchor,head.head,head.goalColumn)});if(selection.eq(view.state.selection))return false;view.dispatch(setSel(view.state,selection));return true}function selectByChar(view,forward){return extendSel(view,range=>view.moveByChar(range,forward))}const selectCharLeft=view=>selectByChar(view,!ltrAtCursor(view));const selectCharRight=view=>selectByChar(view,ltrAtCursor(view));const selectCharForward=view=>selectByChar(view,true);const selectCharBackward=view=>selectByChar(view,false);function selectByGroup(view,forward){return extendSel(view,range=>view.moveByGroup(range,forward))}const selectGroupLeft=view=>selectByGroup(view,!ltrAtCursor(view));const selectGroupRight=view=>selectByGroup(view,ltrAtCursor(view));const selectGroupForward=view=>selectByGroup(view,true);const selectGroupBackward=view=>selectByGroup(view,false);function selectBySubword(view,forward){return extendSel(view,range=>moveBySubword(view,range,forward))}const selectSubwordForward=view=>selectBySubword(view,true);const selectSubwordBackward=view=>selectBySubword(view,false);const selectSyntaxLeft=view=>extendSel(view,range=>moveBySyntax(view.state,range,!ltrAtCursor(view)));const selectSyntaxRight=view=>extendSel(view,range=>moveBySyntax(view.state,range,ltrAtCursor(view)));function selectByLine(view,forward){return extendSel(view,range=>view.moveVertically(range,forward))}const selectLineUp=view=>selectByLine(view,false);const selectLineDown=view=>selectByLine(view,true);function selectByPage(view,forward){return extendSel(view,range=>view.moveVertically(range,forward,pageHeight(view)))}const selectPageUp=view=>selectByPage(view,false);const selectPageDown=view=>selectByPage(view,true);const selectLineBoundaryForward=view=>extendSel(view,range=>moveByLineBoundary(view,range,true));const selectLineBoundaryBackward=view=>extendSel(view,range=>moveByLineBoundary(view,range,false));const selectLineBoundaryLeft=view=>extendSel(view,range=>moveByLineBoundary(view,range,!ltrAtCursor(view)));const selectLineBoundaryRight=view=>extendSel(view,range=>moveByLineBoundary(view,range,ltrAtCursor(view)));const selectLineStart=view=>extendSel(view,range=>EditorSelection.cursor(view.lineBlockAt(range.head).from));const selectLineEnd=view=>extendSel(view,range=>EditorSelection.cursor(view.lineBlockAt(range.head).to));const cursorDocStart=({state,dispatch})=>{dispatch(setSel(state,{anchor:0}));return true};const cursorDocEnd=({state,dispatch})=>{dispatch(setSel(state,{anchor:state.doc.length}));return true};const selectDocStart=({state,dispatch})=>{dispatch(setSel(state,{anchor:state.selection.main.anchor,head:0}));return true};const selectDocEnd=({state,dispatch})=>{dispatch(setSel(state,{anchor:state.selection.main.anchor,head:state.doc.length}));return true};const selectAll=({state,dispatch})=>{dispatch(state.update({selection:{anchor:0,head:state.doc.length},userEvent:"select"}));return true};const selectLine=({state,dispatch})=>{let ranges=selectedLineBlocks(state).map(({from,to})=>EditorSelection.range(from,Math.min(to+1,state.doc.length)));dispatch(state.update({selection:EditorSelection.create(ranges),userEvent:"select"}));return true};const selectParentSyntax=({state,dispatch})=>{let selection=updateSel(state.selection,range=>{var _a;let context=syntaxTree(state).resolveInner(range.head,1);while(!(context.from<range.from&&context.to>=range.to||context.to>range.to&&context.from<=range.from||!((_a=context.parent)===null||_a===void 0?void 0:_a.parent)))context=context.parent;return EditorSelection.range(context.to,context.from)});dispatch(setSel(state,selection));return true};const simplifySelection=({state,dispatch})=>{let cur=state.selection,selection=null;if(cur.ranges.length>1)selection=EditorSelection.create([cur.main]);else if(!cur.main.empty)selection=EditorSelection.create([EditorSelection.cursor(cur.main.head)]);if(!selection)return false;dispatch(setSel(state,selection));return true};function deleteBy(target,by){if(target.state.readOnly)return false;let event="delete.selection",{state}=target;let changes=state.changeByRange(range=>{let{from,to}=range;if(from==to){let towards=by(from);if(towards<from){event="delete.backward";towards=skipAtomic(target,towards,false)}else if(towards>from){event="delete.forward";towards=skipAtomic(target,towards,true)}from=Math.min(from,towards);to=Math.max(to,towards)}else{from=skipAtomic(target,from,false);to=skipAtomic(target,to,true)}return from==to?{range:range}:{changes:{from:from,to:to},range:EditorSelection.cursor(from)}});if(changes.changes.empty)return false;target.dispatch(state.update(changes,{scrollIntoView:true,userEvent:event,effects:event=="delete.selection"?EditorView.announce.of(state.phrase("Selection deleted")):undefined}));return true}function skipAtomic(target,pos,forward){if(target instanceof EditorView)for(let ranges of target.state.facet(EditorView.atomicRanges).map(f=>f(target)))ranges.between(pos,pos,(from,to)=>{if(from<pos&&to>pos)pos=forward?to:from});return pos}const deleteByChar=(target,forward)=>deleteBy(target,pos=>{let{state}=target,line=state.doc.lineAt(pos),before,targetPos;if(!forward&&pos>line.from&&pos<line.from+200&&!/[^ \t]/.test(before=line.text.slice(0,pos-line.from))){if(before[before.length-1]=="\t")return pos-1;let col=countColumn(before,state.tabSize),drop=col%getIndentUnit(state)||getIndentUnit(state);for(let i=0;i<drop&&before[before.length-1-i]==" ";i++)pos--;targetPos=pos}else{targetPos=findClusterBreak(line.text,pos-line.from,forward,forward)+line.from;if(targetPos==pos&&line.number!=(forward?state.doc.lines:1))targetPos+=forward?1:-1}return targetPos});const deleteCharBackward=view=>deleteByChar(view,false);const deleteCharForward=view=>deleteByChar(view,true);const deleteByGroup=(target,forward)=>deleteBy(target,start=>{let pos=start,{state}=target,line=state.doc.lineAt(pos);let categorize=state.charCategorizer(pos);for(let cat=null;;){if(pos==(forward?line.to:line.from)){if(pos==start&&line.number!=(forward?state.doc.lines:1))pos+=forward?1:-1;break}let next=findClusterBreak(line.text,pos-line.from,forward)+line.from;let nextChar=line.text.slice(Math.min(pos,next)-line.from,Math.max(pos,next)-line.from);let nextCat=categorize(nextChar);if(cat!=null&&nextCat!=cat)break;if(nextChar!=" "||pos!=start)cat=nextCat;pos=next}return pos});const deleteGroupBackward=target=>deleteByGroup(target,false);const deleteGroupForward=target=>deleteByGroup(target,true);const deleteToLineEnd=view=>deleteBy(view,pos=>{let lineEnd=view.lineBlockAt(pos).to;return pos<lineEnd?lineEnd:Math.min(view.state.doc.length,pos+1)});const deleteToLineStart=view=>deleteBy(view,pos=>{let lineStart=view.lineBlockAt(pos).from;return pos>lineStart?lineStart:Math.max(0,pos-1)});const deleteTrailingWhitespace=({state,dispatch})=>{if(state.readOnly)return false;let changes=[];for(let pos=0,prev="",iter=state.doc.iter();;){iter.next();if(iter.lineBreak||iter.done){let trailing=prev.search(/\s+$/);if(trailing>-1)changes.push({from:pos-(prev.length-trailing),to:pos});if(iter.done)break;prev=""}else{prev=iter.value}pos+=iter.value.length}if(!changes.length)return false;dispatch(state.update({changes:changes,userEvent:"delete"}));return true};const splitLine=({state,dispatch})=>{if(state.readOnly)return false;let changes=state.changeByRange(range=>{return{changes:{from:range.from,to:range.to,insert:Text.of(["",""])},range:EditorSelection.cursor(range.from)}});dispatch(state.update(changes,{scrollIntoView:true,userEvent:"input"}));return true};const transposeChars=({state,dispatch})=>{if(state.readOnly)return false;let changes=state.changeByRange(range=>{if(!range.empty||range.from==0||range.from==state.doc.length)return{range:range};let pos=range.from,line=state.doc.lineAt(pos);let from=pos==line.from?pos-1:findClusterBreak(line.text,pos-line.from,false)+line.from;let to=pos==line.to?pos+1:findClusterBreak(line.text,pos-line.from,true)+line.from;return{changes:{from:from,to:to,insert:state.doc.slice(pos,to).append(state.doc.slice(from,pos))},range:EditorSelection.cursor(to)}});if(changes.changes.empty)return false;dispatch(state.update(changes,{scrollIntoView:true,userEvent:"move.character"}));return true};function selectedLineBlocks(state){let blocks=[],upto=-1;for(let range of state.selection.ranges){let startLine=state.doc.lineAt(range.from),endLine=state.doc.lineAt(range.to);if(!range.empty&&range.to==endLine.from)endLine=state.doc.lineAt(range.to-1);if(upto>=startLine.number){let prev=blocks[blocks.length-1];prev.to=endLine.to;prev.ranges.push(range)}else{blocks.push({from:startLine.from,to:endLine.to,ranges:[range]})}upto=endLine.number+1}return blocks}function moveLine(state,dispatch,forward){if(state.readOnly)return false;let changes=[],ranges=[];for(let block of selectedLineBlocks(state)){if(forward?block.to==state.doc.length:block.from==0)continue;let nextLine=state.doc.lineAt(forward?block.to+1:block.from-1);let size=nextLine.length+1;if(forward){changes.push({from:block.to,to:nextLine.to},{from:block.from,insert:nextLine.text+state.lineBreak});for(let r of block.ranges)ranges.push(EditorSelection.range(Math.min(state.doc.length,r.anchor+size),Math.min(state.doc.length,r.head+size)))}else{changes.push({from:nextLine.from,to:block.from},{from:block.to,insert:state.lineBreak+nextLine.text});for(let r of block.ranges)ranges.push(EditorSelection.range(r.anchor-size,r.head-size))}}if(!changes.length)return false;dispatch(state.update({changes:changes,scrollIntoView:true,selection:EditorSelection.create(ranges,state.selection.mainIndex),userEvent:"move.line"}));return true}const moveLineUp=({state,dispatch})=>moveLine(state,dispatch,false);const moveLineDown=({state,dispatch})=>moveLine(state,dispatch,true);function copyLine(state,dispatch,forward){if(state.readOnly)return false;let changes=[];for(let block of selectedLineBlocks(state)){if(forward)changes.push({from:block.from,insert:state.doc.slice(block.from,block.to)+state.lineBreak});else changes.push({from:block.to,insert:state.lineBreak+state.doc.slice(block.from,block.to)})}dispatch(state.update({changes:changes,scrollIntoView:true,userEvent:"input.copyline"}));return true}const copyLineUp=({state,dispatch})=>copyLine(state,dispatch,false);const copyLineDown=({state,dispatch})=>copyLine(state,dispatch,true);const deleteLine=view=>{if(view.state.readOnly)return false;let{state}=view,changes=state.changes(selectedLineBlocks(state).map(({from,to})=>{if(from>0)from--;else if(to<state.doc.length)to++;return{from:from,to:to}}));let selection=updateSel(state.selection,range=>view.moveVertically(range,true)).map(changes);view.dispatch({changes:changes,selection:selection,scrollIntoView:true,userEvent:"delete.line"});return true};const insertNewline=({state,dispatch})=>{dispatch(state.update(state.replaceSelection(state.lineBreak),{scrollIntoView:true,userEvent:"input"}));return true};function isBetweenBrackets(state,pos){if(/\(\)|\[\]|\{\}/.test(state.sliceDoc(pos-1,pos+1)))return{from:pos,to:pos};let context=syntaxTree(state).resolveInner(pos);let before=context.childBefore(pos),after=context.childAfter(pos),closedBy;if(before&&after&&before.to<=pos&&after.from>=pos&&(closedBy=before.type.prop(NodeProp.closedBy))&&closedBy.indexOf(after.name)>-1&&state.doc.lineAt(before.to).from==state.doc.lineAt(after.from).from)return{from:before.to,to:after.from};return null}const insertNewlineAndIndent=newlineAndIndent(false);const insertBlankLine=newlineAndIndent(true);function newlineAndIndent(atEof){return({state,dispatch})=>{if(state.readOnly)return false;let changes=state.changeByRange(range=>{let{from,to}=range,line=state.doc.lineAt(from);let explode=!atEof&&from==to&&isBetweenBrackets(state,from);if(atEof)from=to=(to<=line.to?line:state.doc.lineAt(to)).to;let cx=new IndentContext(state,{simulateBreak:from,simulateDoubleBreak:!!explode});let indent=getIndentation(cx,from);if(indent==null)indent=/^\s*/.exec(state.doc.lineAt(from).text)[0].length;while(to<line.to&&/\s/.test(line.text[to-line.from]))to++;if(explode)({from,to}=explode);else if(from>line.from&&from<line.from+100&&!/\S/.test(line.text.slice(0,from)))from=line.from;let insert=["",indentString(state,indent)];if(explode)insert.push(indentString(state,cx.lineIndent(line.from,-1)));return{changes:{from:from,to:to,insert:Text.of(insert)},range:EditorSelection.cursor(from+1+insert[1].length)}});dispatch(state.update(changes,{scrollIntoView:true,userEvent:"input"}));return true}}function changeBySelectedLine(state,f){let atLine=-1;return state.changeByRange(range=>{let changes=[];for(let pos=range.from;pos<=range.to;){let line=state.doc.lineAt(pos);if(line.number>atLine&&(range.empty||range.to>line.from)){f(line,changes,range);atLine=line.number}pos=line.to+1}let changeSet=state.changes(changes);return{changes:changes,range:EditorSelection.range(changeSet.mapPos(range.anchor,1),changeSet.mapPos(range.head,1))}})}const indentSelection=({state,dispatch})=>{if(state.readOnly)return false;let updated=Object.create(null);let context=new IndentContext(state,{overrideIndentation:start=>{let found=updated[start];return found==null?-1:found}});let changes=changeBySelectedLine(state,(line,changes,range)=>{let indent=getIndentation(context,line.from);if(indent==null)return;if(!/\S/.test(line.text))indent=0;let cur=/^\s*/.exec(line.text)[0];let norm=indentString(state,indent);if(cur!=norm||range.from<line.from+cur.length){updated[line.from]=indent;changes.push({from:line.from,to:line.from+cur.length,insert:norm})}});if(!changes.changes.empty)dispatch(state.update(changes,{userEvent:"indent"}));return true};const indentMore=({state,dispatch})=>{if(state.readOnly)return false;dispatch(state.update(changeBySelectedLine(state,(line,changes)=>{changes.push({from:line.from,insert:state.facet(indentUnit)})}),{userEvent:"input.indent"}));return true};const indentLess=({state,dispatch})=>{if(state.readOnly)return false;dispatch(state.update(changeBySelectedLine(state,(line,changes)=>{let space=/^\s*/.exec(line.text)[0];if(!space)return;let col=countColumn(space,state.tabSize),keep=0;let insert=indentString(state,Math.max(0,col-getIndentUnit(state)));while(keep<space.length&&keep<insert.length&&space.charCodeAt(keep)==insert.charCodeAt(keep))keep++;changes.push({from:line.from+keep,to:line.from+space.length,insert:insert.slice(keep)})}),{userEvent:"delete.dedent"}));return true};const insertTab=({state,dispatch})=>{if(state.selection.ranges.some(r=>!r.empty))return indentMore({state:state,dispatch:dispatch});dispatch(state.update(state.replaceSelection("\t"),{scrollIntoView:true,userEvent:"input"}));return true};const emacsStyleKeymap=[{key:"Ctrl-b",run:cursorCharLeft,shift:selectCharLeft,preventDefault:true},{key:"Ctrl-f",run:cursorCharRight,shift:selectCharRight},{key:"Ctrl-p",run:cursorLineUp,shift:selectLineUp},{key:"Ctrl-n",run:cursorLineDown,shift:selectLineDown},{key:"Ctrl-a",run:cursorLineStart,shift:selectLineStart},{key:"Ctrl-e",run:cursorLineEnd,shift:selectLineEnd},{key:"Ctrl-d",run:deleteCharForward},{key:"Ctrl-h",run:deleteCharBackward},{key:"Ctrl-k",run:deleteToLineEnd},{key:"Ctrl-Alt-h",run:deleteGroupBackward},{key:"Ctrl-o",run:splitLine},{key:"Ctrl-t",run:transposeChars},{key:"Ctrl-v",run:cursorPageDown}];const standardKeymap=[{key:"ArrowLeft",run:cursorCharLeft,shift:selectCharLeft,preventDefault:true},{key:"Mod-ArrowLeft",mac:"Alt-ArrowLeft",run:cursorGroupLeft,shift:selectGroupLeft,preventDefault:true},{mac:"Cmd-ArrowLeft",run:cursorLineBoundaryLeft,shift:selectLineBoundaryLeft,preventDefault:true},{key:"ArrowRight",run:cursorCharRight,shift:selectCharRight,preventDefault:true},{key:"Mod-ArrowRight",mac:"Alt-ArrowRight",run:cursorGroupRight,shift:selectGroupRight,preventDefault:true},{mac:"Cmd-ArrowRight",run:cursorLineBoundaryRight,shift:selectLineBoundaryRight,preventDefault:true},{key:"ArrowUp",run:cursorLineUp,shift:selectLineUp,preventDefault:true},{mac:"Cmd-ArrowUp",run:cursorDocStart,shift:selectDocStart},{mac:"Ctrl-ArrowUp",run:cursorPageUp,shift:selectPageUp},{key:"ArrowDown",run:cursorLineDown,shift:selectLineDown,preventDefault:true},{mac:"Cmd-ArrowDown",run:cursorDocEnd,shift:selectDocEnd},{mac:"Ctrl-ArrowDown",run:cursorPageDown,shift:selectPageDown},{key:"PageUp",run:cursorPageUp,shift:selectPageUp},{key:"PageDown",run:cursorPageDown,shift:selectPageDown},{key:"Home",run:cursorLineBoundaryBackward,shift:selectLineBoundaryBackward,preventDefault:true},{key:"Mod-Home",run:cursorDocStart,shift:selectDocStart},{key:"End",run:cursorLineBoundaryForward,shift:selectLineBoundaryForward,preventDefault:true},{key:"Mod-End",run:cursorDocEnd,shift:selectDocEnd},{key:"Enter",run:insertNewlineAndIndent},{key:"Mod-a",run:selectAll},{key:"Backspace",run:deleteCharBackward,shift:deleteCharBackward},{key:"Delete",run:deleteCharForward},{key:"Mod-Backspace",mac:"Alt-Backspace",run:deleteGroupBackward},{key:"Mod-Delete",mac:"Alt-Delete",run:deleteGroupForward},{mac:"Mod-Backspace",run:deleteToLineStart},{mac:"Mod-Delete",run:deleteToLineEnd}].concat(emacsStyleKeymap.map(b=>({mac:b.key,run:b.run,shift:b.shift})));const defaultKeymap=[{key:"Alt-ArrowLeft",mac:"Ctrl-ArrowLeft",run:cursorSyntaxLeft,shift:selectSyntaxLeft},{key:"Alt-ArrowRight",mac:"Ctrl-ArrowRight",run:cursorSyntaxRight,shift:selectSyntaxRight},{key:"Alt-ArrowUp",run:moveLineUp},{key:"Shift-Alt-ArrowUp",run:copyLineUp},{key:"Alt-ArrowDown",run:moveLineDown},{key:"Shift-Alt-ArrowDown",run:copyLineDown},{key:"Escape",run:simplifySelection},{key:"Mod-Enter",run:insertBlankLine},{key:"Alt-l",mac:"Ctrl-l",run:selectLine},{key:"Mod-i",run:selectParentSyntax,preventDefault:true},{key:"Mod-[",run:indentLess},{key:"Mod-]",run:indentMore},{key:"Mod-Alt-\\",run:indentSelection},{key:"Shift-Mod-k",run:deleteLine},{key:"Shift-Mod-\\",run:cursorMatchingBracket},{key:"Mod-/",run:toggleComment},{key:"Alt-A",run:toggleBlockComment}].concat(standardKeymap);const indentWithTab={key:"Tab",run:indentMore,shift:indentLess};export{blockComment,blockUncomment,copyLineDown,copyLineUp,cursorCharBackward,cursorCharForward,cursorCharLeft,cursorCharRight,cursorDocEnd,cursorDocStart,cursorGroupBackward,cursorGroupForward,cursorGroupLeft,cursorGroupRight,cursorLineBoundaryBackward,cursorLineBoundaryForward,cursorLineBoundaryLeft,cursorLineBoundaryRight,cursorLineDown,cursorLineEnd,cursorLineStart,cursorLineUp,cursorMatchingBracket,cursorPageDown,cursorPageUp,cursorSubwordBackward,cursorSubwordForward,cursorSyntaxLeft,cursorSyntaxRight,defaultKeymap,deleteCharBackward,deleteCharForward,deleteGroupBackward,deleteGroupForward,deleteLine,deleteToLineEnd,deleteToLineStart,deleteTrailingWhitespace,emacsStyleKeymap,history,historyField,historyKeymap,indentLess,indentMore,indentSelection,indentWithTab,insertBlankLine,insertNewline,insertNewlineAndIndent,insertTab,invertedEffects,isolateHistory,lineComment,lineUncomment,moveLineDown,moveLineUp,redo,redoDepth,redoSelection,selectAll,selectCharBackward,selectCharForward,selectCharLeft,selectCharRight,selectDocEnd,selectDocStart,selectGroupBackward,selectGroupForward,selectGroupLeft,selectGroupRight,selectLine,selectLineBoundaryBackward,selectLineBoundaryForward,selectLineBoundaryLeft,selectLineBoundaryRight,selectLineDown,selectLineEnd,selectLineStart,selectLineUp,selectMatchingBracket,selectPageDown,selectPageUp,selectParentSyntax,selectSubwordBackward,selectSubwordForward,selectSyntaxLeft,selectSyntaxRight,simplifySelection,splitLine,standardKeymap,toggleBlockComment,toggleBlockCommentByLine,toggleComment,toggleLineComment,transposeChars,undo,undoDepth,undoSelection};