Skip to content

Commit

Permalink
⚡ Don't use prompt buffer.
Browse files Browse the repository at this point in the history
  • Loading branch information
yukimemi committed Jan 7, 2024
1 parent 6bf8fc6 commit d21885b
Showing 1 changed file with 111 additions and 82 deletions.
193 changes: 111 additions & 82 deletions denops/futago/main.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
// =============================================================================
// File : main.ts
// Author : yukimemi
// Last Change : 2024/01/07 23:32:31.
// Last Change : 2024/01/08 02:00:26.
// =============================================================================

import xdg from "https://deno.land/x/[email protected]/src/mod.deno.ts";
import * as vars from "https://deno.land/x/[email protected]/variable/mod.ts";
import * as helper from "https://deno.land/x/[email protected]/helper/mod.ts";
import * as fn from "https://deno.land/x/[email protected]/function/mod.ts";
import { ensure, is } from "https://deno.land/x/[email protected]/mod.ts";
import * as batch from "https://deno.land/x/[email protected]/batch/mod.ts";
import * as buffer from "https://deno.land/x/[email protected]/buffer/mod.ts";
import * as lambda from "https://deno.land/x/[email protected]/lambda/mod.ts";
import * as option from "https://deno.land/x/[email protected]/option/mod.ts";
import * as datetime from "https://deno.land/[email protected]/datetime/mod.ts";
import * as fn from "https://deno.land/x/[email protected]/function/mod.ts";
import * as helper from "https://deno.land/x/[email protected]/helper/mod.ts";
import * as option from "https://deno.land/x/[email protected]/option/mod.ts";
import * as autocmd from "https://deno.land/x/[email protected]/autocmd/mod.ts";
import * as vars from "https://deno.land/x/[email protected]/variable/mod.ts";
import type { Denops } from "https://deno.land/x/[email protected]/mod.ts";
import xdg from "https://deno.land/x/[email protected]/src/mod.deno.ts";
import { assert, is } from "https://deno.land/x/[email protected]/mod.ts";
import { ensureFile } from "https://deno.land/[email protected]/fs/mod.ts";
import { getLogger, handlers, setup } from "https://deno.land/[email protected]/log/mod.ts";
import * as batch from "https://deno.land/x/[email protected]/batch/mod.ts";
import { join } from "https://deno.land/[email protected]/path/mod.ts";
import { ensureFile } from "https://deno.land/[email protected]/fs/mod.ts";
import type { Denops } from "https://deno.land/x/[email protected]/mod.ts";

import { Futago } from "./futago.ts";

let debug = false;
const buffers = new Map<number, {
buf: buffer.OpenResult;
lines: string[];
}>();

function getNow(): string {
return datetime.format(new Date(), "yyyy-MM-ddTHH-mm-ss.SSS");
}

async function getLastLineNumber(denops: Denops, bufnr: number): Promise<number> {
const lines = await fn.getbufline(denops, bufnr, 1, "$");
return lines.length;
}

export async function main(denops: Denops): Promise<void> {
debug = await vars.g.get(denops, "futago_debug", false);
Expand Down Expand Up @@ -58,40 +71,91 @@ export async function main(denops: Denops): Promise<void> {
async startChat(): Promise<void> {
futago.startChat();

const now = datetime.format(new Date(), "yyyy-MM-ddTHH-mm-ss.SSS");
const now = getNow();
const bufname = `futago://chat/${now}`;
const buf = await buffer.open(denops, bufname);
const bufnr = buf.bufnr;
const buf = await buffer.open(denops, bufname, { opener: "tabnew" });

await batch.batch(denops, async () => {
await option.filetype.setBuffer(
await option.filetype.setBuffer(denops, buf.bufnr, "markdown");
await option.buftype.setBuffer(denops, buf.bufnr, "acwrite");
await option.buflisted.setBuffer(denops, buf.bufnr, true);
await option.swapfile.setBuffer(denops, buf.bufnr, false);
await option.wrap.setBuffer(denops, buf.bufnr, true);
await option.modified.setBuffer(denops, buf.bufnr, false);
await buffer.replace(denops, buf.bufnr, [`You: ${now}`, `-------------`, ``]);
});

await denops.cmd("normal! G");
await denops.cmd("startinsert");

buffers.set(buf.bufnr, { buf, lines: await fn.getbufline(denops, buf.bufnr, 1, "$") });
},

async sendChatMessage(bufnr: unknown): Promise<void> {
assert(bufnr, is.Number);
try {
const bufInfo = buffers.get(bufnr);
if (bufInfo == undefined) {
throw new Error(`Buffer not found: ${bufnr}`);
}
const lines = await fn.getbufline(denops, bufInfo.buf.bufnr, 1, "$");
if (lines === bufInfo.lines) {
return;
}
const startLineIndex = lines.findLastIndex((_, index, obj) =>
obj[index].startsWith("You:") && obj[index + 1] === "-------------"
) + 2;

const prompt = lines.slice(startLineIndex);
logger.debug(`You: ${getNow()}}`);
logger.debug(prompt.join("\n"));
logger.debug(`-------------`);

if (
prompt.every((line) =>
line.trim() === ""
)
) {
return;
}

const result = await futago.sendMessageStream(prompt.join("\n"));
await fn.appendbufline(
denops,
bufnr,
"futago.chat",
bufInfo.buf.bufnr,
await getLastLineNumber(denops, bufInfo.buf.bufnr),
["", `Gemini: ${getNow()}`, "-------------", ""],
);
await option.buftype.setBuffer(denops, bufnr, "prompt");
await option.buflisted.setBuffer(denops, bufnr, true);
await option.swapfile.setBuffer(denops, bufnr, false);
await fn.bufload(denops, bufnr);
await fn.prompt_setprompt(denops, bufnr, `futago >> `);
await denops.cmd(
"call prompt_setcallback(bufnr, function('futago#internal#callback_helper', [denops_name, bufnr, lambda_id]))",
{
bufnr,
denops_name: denops.name,
lambda_id: lambda.register(
denops,
async (p) => {
const prompt = ensure(p, is.String);
await promptCallback(denops, futago, bufnr, prompt);
},
),
},
for await (const chunk of result.stream) {
const chunkText = chunk.text();
logger.debug(chunkText);
let insertLineNum = await getLastLineNumber(denops, bufInfo.buf.bufnr);
const lines = chunkText.split(/\r?\n/);
const lastLine = await fn.getbufline(denops, bufInfo.buf.bufnr, insertLineNum);
await fn.setbufline(denops, bufInfo.buf.bufnr, insertLineNum++, [
lastLine + lines[0],
...lines.slice(1),
]);
}
await fn.appendbufline(
denops,
bufInfo.buf.bufnr,
await getLastLineNumber(denops, bufInfo.buf.bufnr),
["", `You: ${getNow()}`, "-------------", ""],
);
await helper.execute(denops, `tabnew ${bufname}`);
await helper.execute(denops, "setlocal wrap");
await helper.execute(denops, "startinsert");
});

buffers.set(bufInfo.buf.bufnr, {
buf: bufInfo.buf,
lines: await fn.getbufline(denops, bufInfo.buf.bufnr, 1, "$"),
});

await denops.cmd(`redraw!`);
await denops.cmd("normal! G");
} catch (e) {
logger.error(e);
} finally {
await option.modified.setBuffer(denops, bufnr, false);
}
},
};

Expand All @@ -104,48 +168,13 @@ export async function main(denops: Denops): Promise<void> {
command! FutagoStart call s:${denops.name}_notify('startChat', [])
`,
);
}

async function promptCallback(
denops: Denops,
futago: Futago,
bufnr: number,
prompt: string,
) {
const logger = debug ? getLogger("futago-debug") : getLogger("futago");
try {
logger.debug(`# user:`);
logger.debug(prompt);
logger.debug(`-------------`);

if (prompt === "exit" || prompt === "quit") {
await helper.execute(denops, `bunload! ${bufnr}`);
return;
}

const result = await futago.sendMessageStream(prompt);
logger.debug(`# futago:`);
const info = await fn.getbufinfo(denops, bufnr);
let insertLineNum = info[0].linecount - 1;
await fn.appendbufline(denops, bufnr, insertLineNum++, "------------------------------");
await fn.appendbufline(denops, bufnr, insertLineNum++, "");

for await (const chunk of result.stream) {
const chunkText = chunk.text();
logger.debug(chunkText);
const info = await fn.getbufinfo(denops, bufnr);
let insertLineNum = info[0].linecount - 1;
const lines = chunkText.split(/\r?\n/);
const lastLine = await fn.getbufline(denops, bufnr, insertLineNum);
await fn.setbufline(denops, bufnr, insertLineNum++, lastLine + lines[0]);
if (lines.length > 0) {
await fn.appendbufline(denops, bufnr, insertLineNum, lines.slice(1));
}
}
} catch (e) {
logger.error(e);
} finally {
await option.buftype.setBuffer(denops, bufnr, "prompt");
await fn.setbufvar(denops, bufnr, "&modified", 0);
}
await autocmd.group(denops, "futago_chat_buffer", (helper) => {
helper.remove("*");
helper.define(
"BufWriteCmd",
"futago://chat/*",
`call denops#notify("${denops.name}", "sendChatMessage", [bufnr()])`,
);
});
}

0 comments on commit d21885b

Please sign in to comment.