Support line ranges in assistant file links (#1067)
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
createWorkspaceFileTabTarget,
|
||||
normalizeWorkspaceFileLocation,
|
||||
workspaceFileLocationsEqual,
|
||||
} from ".";
|
||||
|
||||
describe("normalizeWorkspaceFileLocation", () => {
|
||||
it("normalizes paths and valid line ranges", () => {
|
||||
expect(
|
||||
normalizeWorkspaceFileLocation({
|
||||
path: "src\\app.ts",
|
||||
lineStart: 12.8,
|
||||
lineEnd: 20.2,
|
||||
}),
|
||||
).toEqual({
|
||||
path: "src/app.ts",
|
||||
lineStart: 12,
|
||||
lineEnd: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it("drops invalid or backwards line ranges", () => {
|
||||
expect(normalizeWorkspaceFileLocation({ path: "src/app.ts", lineStart: -1 })).toEqual({
|
||||
path: "src/app.ts",
|
||||
});
|
||||
expect(
|
||||
normalizeWorkspaceFileLocation({ path: "src/app.ts", lineStart: 20, lineEnd: 12 }),
|
||||
).toEqual({
|
||||
path: "src/app.ts",
|
||||
lineStart: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects empty paths", () => {
|
||||
expect(normalizeWorkspaceFileLocation({ path: " " })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("workspace file tab targets", () => {
|
||||
it("keeps file tab identity separate from line selection", () => {
|
||||
expect(createWorkspaceFileTabTarget({ path: "src/app.ts", lineStart: 12 })).toEqual({
|
||||
kind: "file",
|
||||
path: "src/app.ts",
|
||||
lineStart: 12,
|
||||
});
|
||||
});
|
||||
|
||||
it("compares full location equality", () => {
|
||||
expect(
|
||||
workspaceFileLocationsEqual(
|
||||
{ path: "src/app.ts", lineStart: 12 },
|
||||
{ path: "src/app.ts", lineStart: 12 },
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
workspaceFileLocationsEqual(
|
||||
{ path: "src/app.ts", lineStart: 12 },
|
||||
{ path: "src/app.ts", lineStart: 13 },
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
export type OpenFileDisposition = "main" | "side";
|
||||
|
||||
export interface WorkspaceFileLocation {
|
||||
path: string;
|
||||
lineStart?: number;
|
||||
lineEnd?: number;
|
||||
}
|
||||
|
||||
export type WorkspaceFileTabTarget = { kind: "file" } & WorkspaceFileLocation;
|
||||
|
||||
export interface WorkspaceFileOpenRequest {
|
||||
location: WorkspaceFileLocation;
|
||||
disposition: OpenFileDisposition;
|
||||
}
|
||||
|
||||
export function normalizeWorkspaceFileLocation(
|
||||
location: WorkspaceFileLocation | null | undefined,
|
||||
): WorkspaceFileLocation | null {
|
||||
if (!location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const path = location.path.trim().replace(/\\/g, "/");
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lineStart = normalizeLineNumber(location.lineStart);
|
||||
const lineEnd = normalizeLineNumber(location.lineEnd);
|
||||
return {
|
||||
path,
|
||||
...(lineStart ? { lineStart } : {}),
|
||||
...(lineStart && lineEnd && lineEnd >= lineStart ? { lineEnd } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function workspaceFileLocationsEqual(
|
||||
left: WorkspaceFileLocation,
|
||||
right: WorkspaceFileLocation,
|
||||
): boolean {
|
||||
return (
|
||||
left.path === right.path && left.lineStart === right.lineStart && left.lineEnd === right.lineEnd
|
||||
);
|
||||
}
|
||||
|
||||
export function createWorkspaceFileTabTarget(
|
||||
location: WorkspaceFileLocation,
|
||||
): WorkspaceFileTabTarget {
|
||||
return {
|
||||
kind: "file",
|
||||
...location,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeLineNumber(value: number | null | undefined): number | undefined {
|
||||
return typeof value === "number" && Number.isFinite(value) && value > 0
|
||||
? Math.floor(value)
|
||||
: undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user