Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve findReferences and hover tools #6

Merged
merged 3 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
with:
bun-version-file: ".bun-version"

- name: Install typescript-language-server
run: bun add -g typescript-language-server typescript

- name: Install dependencies
working-directory: ./bun
run: bun install --frozen-lockfile
Expand Down
2 changes: 1 addition & 1 deletion bun/chat/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ ${model.edits[filePath].requestIds.map(
type: "tool-manager-msg",
msg,
}),
)}`,
)}\n`,
)}\n`,
);
}
Expand Down
15 changes: 7 additions & 8 deletions bun/lsp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type Nvim } from "bunvim";
import type { NvimBuffer } from "./nvim/buffer.ts";
import type { PositionString } from "./nvim/window.ts";

export class Lsp {
private requestCounter = 0;
Expand All @@ -21,8 +22,7 @@ export class Lsp {

requestHover(
buffer: NvimBuffer,
row: number,
col: number,
pos: PositionString,
): Promise<LspHoverResponse> {
return new Promise<LspHoverResponse>((resolve, reject) => {
const requestId = this.getRequestId();
Expand All @@ -37,8 +37,8 @@ export class Lsp {
uri = vim.uri_from_bufnr(${buffer.id})
},
position = {
line = ${row},
character = ${col}
line = ${pos.row},
character = ${pos.col}
}
}, function(responses)
require('magenta').lsp_response("${requestId}", responses)
Expand All @@ -54,8 +54,7 @@ export class Lsp {

requestReferences(
buffer: NvimBuffer,
row: number,
col: number,
pos: PositionString,
): Promise<LspReferencesResponse> {
return new Promise((resolve, reject) => {
const requestId = this.getRequestId();
Expand All @@ -70,8 +69,8 @@ export class Lsp {
uri = vim.uri_from_bufnr(${buffer.id})
},
position = {
line = ${row},
character = ${col}
line = ${pos.row},
character = ${pos.col}
},
context = {
includeDeclaration = true
Expand Down
10 changes: 10 additions & 0 deletions bun/nvim/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import { NvimBuffer, type BufNr } from "./buffer.ts";
export type Row0Indexed = number & { __row0Indexed: true };
export type Row1Indexed = number & { __row1Indexed: true };
export type ByteIdx = number & { __byteIdx: true };

/** A coordinate in a js string, which are utf-16 encoded by default. This is the coordinate that lsp clients typically expect.
*/
export type StringIdx = number & { __charIdx: true };

export type PositionString = {
row: Row0Indexed;
col: StringIdx;
};

export type Position1Indexed = {
row: Row1Indexed;
col: ByteIdx;
Expand Down
33 changes: 30 additions & 3 deletions bun/tea/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { Nvim } from "bunvim";
import type { Line, NvimBuffer } from "../nvim/buffer.ts";
import type { ByteIdx, Position0Indexed } from "../nvim/window.ts";
import type {
PositionString,
ByteIdx,
Position0Indexed,
StringIdx,
} from "../nvim/window.ts";

export async function replaceBetweenPositions({
buffer,
Expand Down Expand Up @@ -37,10 +42,10 @@ export async function replaceBetweenPositions({
export function calculatePosition(
startPos: Position0Indexed,
buf: Buffer,
indexInText: number,
indexInText: ByteIdx,
): Position0Indexed {
let { row, col } = startPos;
let currentIndex = 0;
let currentIndex: ByteIdx = 0 as ByteIdx;

while (currentIndex < indexInText) {
// 10 == '\n' in hex
Expand All @@ -56,6 +61,28 @@ export function calculatePosition(
return { row, col };
}

export function calculateStringPosition(
startPos: PositionString,
content: string,
indexInText: StringIdx,
): PositionString {
let { row, col } = startPos;
let currentIndex = 0 as StringIdx;

while (currentIndex < indexInText) {
// 10 == '\n' in hex
if (content[currentIndex] == "\n") {
row++;
col = 0 as StringIdx;
} else {
col++;
}
currentIndex++;
}

return { row, col };
}

export async function logBuffer(buffer: NvimBuffer, context: { nvim: Nvim }) {
const lines = await buffer.getLines({
start: 0,
Expand Down
17 changes: 17 additions & 0 deletions bun/test/fixtures/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
type Nested = {
a: {
b: {
c: "test";
};
};
};

const val: Nested = {
a: {
b: {
c: "test",
},
},
};

console.log(val.a.b.c);
6 changes: 2 additions & 4 deletions bun/tools/diff.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ describe("bun/tools/diff.spec.ts", () => {
name: "replace",
input: {
filePath: "bun/test/fixtures/poem.txt",
match: `Moonlight whispers through the trees,
Silver shadows dance with ease.
Stars above like diamonds bright,
Paint their stories in the night.`,
startLine: `Moonlight whispers through the trees,`,
endLine: `Paint their stories in the night.`,
replace: `In gardens wild and flowing free,
Magenta blooms for all to see.
Nature's canvas, bold and bright,
Expand Down
16 changes: 13 additions & 3 deletions bun/tools/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,23 @@ export async function displayDiffs({
}

case "replace": {
const replaceStart = content.indexOf(edit.input.match);
const replaceEnd = replaceStart + edit.input.match.length;
const replaceStart = content.indexOf(edit.input.startLine);
const replaceEnd =
content.indexOf(edit.input.endLine, replaceStart) +
edit.input.endLine.length;

if (replaceStart == -1) {
dispatch({
type: "error",
message: `Unable to find match parameter ${edit.input.match} in file ${filePath}`,
message: `Unable to find startLine ${edit.input.startLine} in file ${filePath}`,
});
continue;
}

if (replaceEnd == -1) {
dispatch({
type: "error",
message: `Unable to find endLine ${edit.input.endLine} in file ${filePath}`,
});
continue;
}
Expand Down
66 changes: 66 additions & 0 deletions bun/tools/findReferences.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { type ToolRequestId } from "./toolManager.ts";
import { describe, it, expect } from "bun:test";
import { withDriver } from "../test/preamble";
import { pollUntil } from "../utils/async.ts";

describe("bun/tools/findReferences.spec.ts", () => {
it("findReferences end-to-end", async () => {
await withDriver(async (driver) => {
await driver.editFile("bun/test/fixtures/test.ts");
await driver.showSidebar();

await driver.inputMagentaText(`Try finding references for a symbol`);
await driver.send();

const toolRequestId = "id" as ToolRequestId;
await driver.mockAnthropic.respond({
stopReason: "tool_use",
text: "ok, here goes",
toolRequests: [
{
status: "ok",
value: {
type: "tool_use",
id: toolRequestId,
name: "find_references",
input: {
filePath: "bun/test/fixtures/test.ts",
symbol: "val.a.b.c",
},
},
},
],
});

const result = await pollUntil(
() => {
const state = driver.magenta.chatApp.getState();
if (state.status != "running") {
throw new Error(`app crashed`);
}

const toolWrapper =
state.model.toolManager.toolWrappers[toolRequestId];
if (!toolWrapper) {
throw new Error(
`could not find toolWrapper with id ${toolRequestId}`,
);
}

if (toolWrapper.model.state.state != "done") {
throw new Error(`Request not done`);
}

return toolWrapper.model.state.result;
},
{ timeout: 3000 },
);

expect(result).toEqual({
tool_use_id: toolRequestId,
type: "tool_result",
content: `bun/test/fixtures/test.ts:4:6\nbun/test/fixtures/test.ts:12:6\nbun/test/fixtures/test.ts:17:20\n`,
});
});
});
});
Loading
Loading