co:here codecrush

About

Proyecto hecho para la hackaton de midudev en colaboración con co:here.

Usando mi propio editor de código llamado codecrush que ahora mismo esta en la versión 0.0.5 y decidí poner a prueba el sistema de plugins. Entonces a contnuación voy a explicar como lo hice.

La idea es extender el autocompletado que trae el editor por defecto que es bastante limitado.

Extendiendo el editor

Para crear una extensión del editor tenemos que importar Component desde el core package de codecrush y crear una clase extendiéndola con el Component

AiAutocompletion.ts
Copy

_10
import { Component } from "codecrush-core";
_10
_10
export class CohereAutoCompletion extends Component {
_10
constructor() {
_10
super("cohere-autocompletion");
_10
}
_10
}

Petición a co:here

Creamos la función que se va a encargar de hacer el fetching de datos. La misma recibe como parámetro la palabra actual sobre la cual está posicionado el cursor del editor. Lo cual es algo que el mismo editor nos lo da.

AiAutocompletion.ts
Copy

_48
import { Component } from "codecrush-core";
_48
_48
export class CohereAutoCompletion extends Component {
_48
signal: AbortController | null;
_48
constructor() {
_48
super("cohere-autocompletion");
_48
_48
this.signal = null;
_48
}
_48
_48
async fetchAutoCompletions(currentWord: string) {
_48
this.signal = new AbortController();
_48
return fetch("https://api.cohere.ai/generate", {
_48
method: "POST",
_48
headers: {
_48
"Cohere-Version": "2022-12-06",
_48
accept: "application/json",
_48
authorization: `Bearer ${process.env.NEXT_PUBLIC_COHERE_TOKEN}`,
_48
"content-type": "application/json",
_48
},
_48
signal: this.signal.signal,
_48
body: JSON.stringify({
_48
max_tokens: 100,
_48
return_likelihoods: "NONE",
_48
truncate: "END",
_48
prompt: `this program returns text autocompletions for code editor, it can have multiple autocompletions and should only return javascript code like this
_48
--
_48
input:spl
_48
autocompletion:splice,slice
_48
--
_48
input:con
_48
autocompletion:const,console,continue
_48
--
_48
input:for
_48
autocompletion:for(let i = 0; i < array.length, i++),forEach,for(const a in array)
_48
--
_48
input:${currentWord}
_48
autocompletion:`,
_48
temperature: 0.2,
_48
k: 0,
_48
p: 1,
_48
frequency_penalty: 0,
_48
presence_penalty: 0,
_48
stop_sequences: ["--"],
_48
}),
_48
});
_48
}
_48
}

Accediendo a los componentes por defecto del editor

En este ejemplo vamos a tener que usar dos componentes del editor

  • autocompletion: Para añadir los resultados cuando co:here nos traiga los datos.
  • activity-bar: Para añadir el evento a la barra de actividades y dar feedback al usuario de cuando se esta haciendo la petición a co:here

El editor tiene una función llamada getComponent con la que accederemos a esos componentes. Así como el siguiente ejemplo en el que registraremos algo en la barra de actividades.


_10
import { ActivityBarComponent } from "codecrush-core";
_10
const activityBar = this.editor.getComponent<ActivityBarComponent>("activity-bar");
_10
_10
activityBar.registerActivity(
_10
"cohere-activity", // -> Id para la actividad
_10
`co:here -> (${completion.currentWord})`, // -> Texto que deseamos poner
_10
true // -> True si queremos que aparezca un spinner
_10
);

Importamos desde el core package ActivityBarComponent para tener autocompletado sobre las funciones que ya trae activityBar.

En nuestro componente lo haremos creando una función init que va a ser la que va inicializar getResultsFromAi usando debounce y la que va añadir los resultados al editor también.

AiAutocompletion.ts
Copy

_60
import {
_60
ActivityBarComponent,
_60
AutocompletionComponent,
_60
Component,
_60
} from "codecrush-core";
_60
import debounce from "@/utils/debounce";
_60
_60
export class CohereAutoCompletion extends Component {
_60
getResultsFromAi: () => void | null;
_60
signal: AbortController | null;
_60
constructor() {
_60
super("cohere-autocompletion");
_60
this.signal = null;
_60
this.getResultsFromAi = () => {}
_60
_60
this.init();
_60
}
_60
_60
init() {
_60
this.getResultsFromAi = debounce(() => {
_60
if (!process.env.NEXT_PUBLIC_COHERE_TOKEN) return;
_60
const completion =
_60
this.editor.getComponent<AutocompletionComponent>("autocompletion");
_60
const activityBar =
_60
this.editor.getComponent<ActivityBarComponent>("activity-bar");
_60
if (this.signal) {
_60
this.signal.abort();
_60
this.signal = null;
_60
}
_60
_60
activityBar.registerActivity(
_60
"cohere-activity",
_60
`co:here -> (${completion.currentWord})`,
_60
true
_60
);
_60
this.fetchAutoCompletions(completion.currentWord)
_60
.then((res) => res.json())
_60
.then((data) => {
_60
const suggestions = data.generations[0].text
_60
.replace("\n", "")
_60
.replace("--", "")
_60
.split(",")
_60
.map((x: string) => x.trim())
_60
.filter((x: string) => x !== "");
_60
suggestions.forEach((item: string) => {
_60
completion.results.push({ suggestion: item, owner: "co:here" });
_60
});
_60
completion.render();
_60
activityBar.removeActivity("cohere-activity");
_60
})
_60
.catch(() => {
_60
activityBar.removeActivity("cohere-activity");
_60
});
_60
}, 500);
_60
}
_60
_60
async fetchAutoCompletions(currentWord: string) {
_60
...
_60
}
_60
}

Editor events

Gracias a que estamos extendiendo la clase con Component, tenemos los dos eventos que vamos a ejecutar para pedir los resultados o en su defecto para cancelar la petición con el AbortController

AiAutocompletion.ts
Copy

_37
import {
_37
ActivityBarComponent,
_37
AutocompletionComponent,
_37
Component,
_37
} from "codecrush-core";
_37
import debounce from "@/utils/debounce";
_37
_37
export class CohereAutoCompletion extends Component {
_37
getResultsFromAi: () => void | null;
_37
signal: AbortController | null;
_37
constructor() {
_37
super("cohere-autocompletion");
_37
this.signal = null;
_37
this.getResultsFromAi = () => {}
_37
_37
this.init();
_37
}
_37
_37
init() {
_37
...
_37
}
_37
_37
async fetchAutoCompletions(currentWord: string) {
_37
...
_37
}
_37
_37
onSearchSuggestions(): void {
_37
this.getResultsFromAi();
_37
}
_37
_37
onAutoCompletionCancel(): void {
_37
if (this.signal) {
_37
this.signal.abort();
_37
this.signal = null;
_37
}
_37
}
_37
}

Y con eso tendriamos un autocompletado extendido con co:here gracias a el sistema de plugins del editor.

React component

Para usar el componente que acabamos de crear simplemente se lo pasamos al Editor como prop.

YourComponent.tsx
Copy

_13
import { CohereAutoCompletion } from "@/editorPlugins/AiAutocompletion";
_13
import { Editor } from "codecrush-react";
_13
_13
export const EditorWrapper = () => {
_13
return (
_13
<Editor
_13
height={400}
_13
id="js-editor"
_13
theme="material-ocean"
_13
components={[CohereAutoCompletion]}
_13
/>
_13
);
_13
};

Hecho con amor by JosueRhea