monacopilot
Version:
AI auto-completion plugin for Monaco Editor
21 lines (18 loc) • 10.4 kB
JavaScript
import {Copilot,logger}from'@monacopilot/core';var B=n=>!n||n.length===0?"":n.length===1?n[0]:`${n.slice(0,-1).join(", ")} and ${n.slice(-1)}`;var D=(n,e,t={})=>{if(e<=0)return "";let o=n.split(`
`),r=o.length;if(e>=r)return n;if(t.truncateDirection==="keepEnd"){let s=o.slice(-e);return s.every(a=>a==="")?`
`.repeat(e):s.join(`
`)}let i=o.slice(0,e);return i.every(s=>s==="")?`
`.repeat(e):i.join(`
`)};var re="<|developer_cursor_is_here|>",U=n=>({instruction:ie(),context:se(n),fileContent:ae(n)}),ie=()=>"Provide concise and readable code completions that are syntactically and logically accurate, and seamlessly integrate with the existing context. Output only the raw code to be inserted at the cursor location without any additional text, comments, or text before or after the cursor.",se=n=>{let{technologies:e=[],filename:t,relatedFiles:o=[],language:r}=n,i=B([r,...e].filter(l=>!!l)),s=o.length===0?"":o.map(({path:l,content:c})=>`### ${l}
${c}`).join(`
`),a=[i?`Technology stack: ${i}`:"",`File: ${t||"unknown"}`].filter(Boolean).join(`
`);return `${s?`${s}
`:""}${a}`},ae=n=>{let{textBeforeCursor:e,textAfterCursor:t}=n;return `**Current code:**
\`\`\`
${e}${re}${t}
\`\`\``};var T=class extends Copilot{async complete(e){let{body:t,options:o}=e,{customPrompt:r,headers:i}=o??{},{completionMetadata:s}=t,{text:a,raw:l,error:c}=await this.makeAIRequest(s,{customPrompt:r,customHeaders:i});return {completion:a,raw:l,error:c}}getDefaultPrompt(e){return U(e)}};var q=100,K=true,v="onIdle",V=true,H=120,$=400,W=0;var h=(n,e)=>e.getValueInRange({startLineNumber:1,startColumn:1,endLineNumber:n.lineNumber,endColumn:n.column}),z=(n,e)=>e.getValueInRange({startLineNumber:n.lineNumber,startColumn:n.column,endLineNumber:e.getLineCount(),endColumn:e.getLineMaxColumn(e.getLineCount())}),G=n=>n.getValue();var P=class{constructor(e){this.capacity=e;this.head=0;this.tail=0;this.size=0;this.buffer=new Array(e);}enqueue(e){let t;return this.size===this.capacity&&(t=this.dequeue()),this.buffer[this.tail]=e,this.tail=(this.tail+1)%this.capacity,this.size++,t}dequeue(){if(this.size===0)return;let e=this.buffer[this.head];return this.buffer[this.head]=void 0,this.head=(this.head+1)%this.capacity,this.size--,e}getAll(){return this.buffer.filter(e=>e!==void 0)}clear(){this.buffer=new Array(this.capacity),this.head=0,this.tail=0,this.size=0;}getSize(){return this.size}isEmpty(){return this.size===0}isFull(){return this.size===this.capacity}};var L=class L{constructor(){this.cache=new P(L.MAX_CACHE_SIZE);}get(e,t){return this.cache.getAll().filter(o=>this.isValidCacheItem(o,e,t))}add(e){e.completion.trim()&&this.cache.enqueue(e);}clear(){this.cache.clear();}isValidCacheItem(e,t,o){let r=e.textBeforeCursor.trim(),i=h(t,o),s=i,a=o.getLineContent(t.lineNumber);if(t.column===a.length+1&&t.lineNumber<o.getLineCount()){let c=o.getLineContent(t.lineNumber+1);s=i+`
`+c;}if(!(s.trim().includes(r)||r.includes(s.trim())))return false;let l=o.getValueInRange(e.range);return this.isPartialMatch(l,e.completion)?this.isPositionValid(e,t):false}isPartialMatch(e,t){let o=e.trim(),r=t.trim();return r.startsWith(o)||o.startsWith(r)}isPositionValid(e,t){let{range:o}=e,{startLineNumber:r,startColumn:i,endLineNumber:s,endColumn:a}=o,{lineNumber:l,column:c}=t;return l<r||l>s?false:r===s?c>=i-1&&c<=a+1:l===r?c>=i-1:l===s?c<=a+1:true}};L.MAX_CACHE_SIZE=20;var M=L;var O=class{constructor(e){this.formattedCompletion="";this.formattedCompletion=e;}setCompletion(e){return this.formattedCompletion=e,this}removeInvalidLineBreaks(){return this.formattedCompletion=this.formattedCompletion.trimEnd(),this}removeMarkdownCodeSyntax(){return this.formattedCompletion=this.removeMarkdownCodeBlocks(this.formattedCompletion),this}removeMarkdownCodeBlocks(e){let t=e.split(`
`),o=[],r=false;for(let i=0;i<t.length;i++){let s=t[i],a=s.trim().startsWith("```");if(a&&!r){r=true;continue}if(a&&r){r=false;continue}o.push(s);}return o.join(`
`)}removeExcessiveNewlines(){return this.formattedCompletion=this.formattedCompletion.replace(/\n{3,}/g,`
`),this}build(){return this.formattedCompletion}};var I=class{findOverlaps(e,t,o){if(!e)return {startOverlapLength:0,maxOverlapLength:0};let r=e.length,i=t.length,s=o.length,a=0,l=0,c=0,m=Math.min(r,i);for(let p=1;p<=m;p++){let f=e.substring(0,p),u=t.slice(-p);f===u&&(c=p);}let C=Math.min(r,s);for(let p=0;p<C&&e[p]===o[p];p++)a++;for(let p=1;p<=C;p++)e.slice(-p)===o.slice(0,p)&&(l=p);let d=Math.max(a,l);if(d===0){for(let p=1;p<r;p++)if(o.startsWith(e.substring(p))){d=r-p;break}}return {startOverlapLength:c,maxOverlapLength:d}}};var A=class{constructor(e){this.monaco=e;this.textOverlapCalculator=new I;}computeInsertionRange(e,t,o){if(!t)return this.createEmptyRange(e);let r=o.getOffsetAt(e),i=o.getValue().substring(0,r),s=o.getValue().substring(r);if(r>=o.getValue().length)return this.createEmptyRange(e);if(s.length===0)return this.createEmptyRange(e);let{startOverlapLength:a,maxOverlapLength:l}=this.textOverlapCalculator.findOverlaps(t,i,s),c=a>0?o.getPositionAt(r-a):e,m=r+l,C=o.getPositionAt(m);return new this.monaco.Range(c.lineNumber,c.column,C.lineNumber,C.column)}computeCacheRange(e,t){let o=e.lineNumber,r=e.column,i=t.split(`
`),s=i.length-1,a=o+s,l=s===0?r+i[0].length:i[s].length+1;return new this.monaco.Range(o,r,a,l)}createEmptyRange(e){return new this.monaco.Range(e.lineNumber,e.column,e.lineNumber,e.column)}};var X=async n=>{let{endpoint:e,body:t}=n,o=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!o.ok)throw new Error(`Error while fetching completion item: ${o.statusText}`);let{completion:r,error:i}=await o.json();if(i)throw new Error(i);return {completion:r}},Y=({pos:n,mdl:e,options:t})=>{let{filename:o,language:r,technologies:i,relatedFiles:s,maxContextLines:a=q}=t,c=s&&s.length>0?3:2,m=a?Math.floor(a/c):void 0,C=(g,y,w)=>{let b=g(n,e);return y?D(b,y,w):b},d=(g,y)=>!g||!y?g:g.map(({content:w,...b})=>({...b,content:D(w,y)})),p=C(h,m,{truncateDirection:"keepEnd"}),f=C(z,m,{truncateDirection:"keepStart"}),u=d(s,m);return {filename:o,language:r,technologies:i,relatedFiles:u,textBeforeCursor:p,textAfterCursor:f,cursorPosition:n}};var Z=(n,e=300)=>{let t=null,o=null,r=(...i)=>{if(o)return o.args=i,o.promise;let s,a,l=new Promise((c,m)=>{s=c,a=m;});return o={args:i,promise:l,resolve:s,reject:a},t&&(clearTimeout(t),t=null),t=setTimeout(async()=>{let c=o;if(c){o=null,t=null;try{let m=await n(...c.args);c.resolve(m);}catch(m){c.reject(m);}}},e),l};return r.cancel=()=>{t&&(clearTimeout(t),t=null),o&&(o.reject(new Error("Cancelled")),o=null);},r};var J=n=>typeof n=="string"?n==="Cancelled"||n==="AbortError":n instanceof Error?n.message==="Cancelled"||n.name==="AbortError":false;var E=n=>({items:n,enableForwardStability:true});var S=new M,Q=async({monaco:n,mdl:e,pos:t,token:o,isCompletionAccepted:r,options:i})=>{let{trigger:s=v,endpoint:a,enableCaching:l=K,allowFollowUpCompletions:c=V,onError:m,requestHandler:C}=i;if(l&&!r){let d=S.get(t,e).map(p=>({insertText:p.completion,range:p.range}));if(d.length>0)return E(d)}if(o.isCancellationRequested||!c&&r)return E([]);try{let d=Z(u=>(i.onCompletionRequested?.(u),C?.(u)??X(u)),{onTyping:H,onIdle:$,onDemand:W}[s]);o.onCancellationRequested(()=>{d.cancel();});let p=Y({pos:t,mdl:e,options:i}),{completion:f}=await d({endpoint:a,body:{completionMetadata:p}});if(f){let u=new O(f).removeMarkdownCodeSyntax().removeExcessiveNewlines().removeInvalidLineBreaks().build(),g=new A(n);return l&&S.add({completion:u,range:g.computeCacheRange(t,u),textBeforeCursor:h(t,e)}),E([{insertText:u,range:g.computeInsertionRange(t,u,e)}])}}catch(d){if(J(d))return E([]);m?m(d):logger.warn("Cannot provide completion",d);}return E([])};var F=new WeakMap,x=n=>F.get(n),ee=(n,e)=>{F.set(n,e);},N=n=>{F.delete(n);},te=()=>({isCompletionAccepted:false,isCompletionVisible:false,isExplicitlyTriggered:false,hasRejectedCurrentCompletion:false});var oe=(n,e,t)=>{let o=x(e);return o?n.languages.registerInlineCompletionsProvider(t.language,{provideInlineCompletions:(r,i,s,a)=>{if(!(t.trigger==="onDemand"&&!o.isExplicitlyTriggered||t.triggerIf&&!t.triggerIf({text:G(e),position:i,triggerType:t.trigger??v})))return Q({monaco:n,mdl:r,pos:i,token:a,isCompletionAccepted:o.isCompletionAccepted,options:t})},handleItemDidShow:(r,i,s)=>{o.isExplicitlyTriggered=false,o.hasRejectedCurrentCompletion=false,!o.isCompletionAccepted&&(o.isCompletionVisible=true,t.onCompletionShown?.(s,i.range));},freeInlineCompletions:()=>{}}):null};var ce={TAB:(n,e)=>e.keyCode===n.KeyCode.Tab,CMD_RIGHT_ARROW:(n,e)=>e.keyCode===n.KeyCode.RightArrow&&e.metaKey},_=class{constructor(e,t,o){this.monaco=e;this.state=t;this.options=o;}handleKeyEvent(e){let t={monaco:this.monaco,event:e,state:this.state,options:this.options};this.handleCompletionAcceptance(t),this.handleCompletionRejection(t);}handleCompletionAcceptance(e){return e.state.isCompletionVisible&&this.isAcceptanceKey(e.event)?(e.options.onCompletionAccepted?.(),e.state.isCompletionAccepted=true,e.state.isCompletionVisible=false,true):(e.state.isCompletionAccepted=false,false)}handleCompletionRejection(e){return this.shouldRejectCompletion(e)?(e.options.onCompletionRejected?.(),e.state.hasRejectedCurrentCompletion=true,true):false}shouldRejectCompletion(e){return e.state.isCompletionVisible&&!e.state.hasRejectedCurrentCompletion&&!e.state.isCompletionAccepted&&!this.isAcceptanceKey(e.event)}isAcceptanceKey(e){return Object.values(ce).some(t=>t(this.monaco,e))}},ne=(n,e,t,o)=>{let r=new _(n,t,o);return e.onKeyDown(i=>r.handleKeyEvent(i))};var R=null,me=(n,e,t)=>{R&&R.deregister();let o=[];ee(e,te()),e.updateOptions({inlineSuggest:{enabled:true}});try{let r=x(e);if(!r)return logger.warn("Completion is not registered properly. State not found."),ue();let i=oe(n,e,t);i&&o.push(i);let s=ne(n,e,r,t);o.push(s);let a={deregister:()=>{o.forEach(l=>l.dispose()),S.clear(),N(e),R=null;},trigger:()=>de(e)};return R=a,a}catch(r){return t.onError?t.onError(r):logger.report(r),{deregister:()=>{o.forEach(i=>i.dispose()),N(e),R=null;},trigger:()=>{}}}},de=n=>{let e=x(n);if(!e){logger.warn("Completion is not registered. Use `registerCompletion` to register completion first.");return}e.isExplicitlyTriggered=true,n.trigger("keyboard","editor.action.inlineSuggest.trigger",{});},ue=()=>({deregister:()=>{},trigger:()=>{}});var ht=T;export{T as CompletionCopilot,ht as Copilot,me as registerCompletion};