Skip to content

Instantly share code, notes, and snippets.

@laiso
Created December 18, 2023 17:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laiso/8c8c3efd8ddd75abb9e92497fc38eb0a to your computer and use it in GitHub Desktop.
Save laiso/8c8c3efd8ddd75abb9e92497fc38eb0a to your computer and use it in GitHub Desktop.
Retrieve completion suggestions from GitHub Copilot programmatically in a Node.js program
import path from "node:path";
import fs from "node:fs";
import { spawn } from "node:child_process";
/**
* $ node getCompletionsCycling.mjs ./sample.ts
*/
const server = spawn("node", ["./copilot.vim/dist/agent.js"]); // https://github.com/github/copilot.vim
const sendMessage = (data) => {
const dataString = JSON.stringify({ ...data, jsonrpc: "2.0" });
const contentLength = Buffer.byteLength(dataString, "utf8");
const rpcString = `Content-Length: ${contentLength}\r\n\r\n${dataString}`;
server.stdin.write(rpcString);
};
let requestId = 0;
const resolveMap = new Map();
const rejectMap = new Map();
const sendRequest = (method, params) => {
sendMessage({ id: ++requestId, method, params });
return new Promise((resolve, reject) => {
resolveMap.set(requestId, resolve);
rejectMap.set(requestId, reject);
});
};
const sendNotification = (method, params) => {
sendMessage({ method, params });
};
const handleReceivedPayload = (payload) => {
// console.log("Received payload:", payload);
if ("id" in payload) {
if ("result" in payload) {
const resolve = resolveMap.get(payload.id);
if (resolve) {
resolve(payload.result);
resolveMap.delete(payload.id);
}
} else if ("error" in payload) {
const reject = rejectMap.get(payload.id);
if (reject) {
reject(payload.error);
rejectMap.delete(payload.id);
}
}
}
};
server.stdout.on("data", (data) => {
const rawString = data.toString("utf-8");
const payloadStrings = rawString
.split(/Content-Length: \d+\r\n\r\n/)
.filter((s) => s);
for (const payloadString of payloadStrings) {
let payload;
payload = JSON.parse(payloadString);
handleReceivedPayload(payload);
}
});
const main = async () => {
const filePath = process.argv[2];
const languageId = "plaintext"; // e.g. typescript
const text = fs.readFileSync(filePath, "utf-8");
await sendRequest("initialize", {
capabilities: { workspace: { workspaceFolders: true } },
});
sendNotification("textDocument/didOpen", {
textDocument: {
uri: `file://${path.resolve(filePath)}`,
languageId,
version: 0,
text,
},
});
const completions = await sendRequest("getCompletionsCycling", {
doc: {
version: 0,
position: { line: 2, character: 1 },
uri: `file://${path.resolve(filePath)}`,
},
});
console.log("Completions:", completions);
process.exit(0);
};
void main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment