mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-10-29 18:52:45 +08:00
fix(vscode): fix codebase indexing status update on windows
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Fix stale indexing status updates in the VS Code extension on Windows so Settings and the chat indicator refresh while indexing progresses. Directory matching for indexing SSE events now handles Windows path casing, and indexing status pushes are no longer dropped if they arrive before the initial config payload.
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
resolveContextDirectory,
|
||||
resolveNewSessionDirectory,
|
||||
resolveWorkspaceDirectory,
|
||||
sameDirectory,
|
||||
SessionStreamScheduler,
|
||||
type SessionRefreshContext,
|
||||
} from "./kilo-provider-utils"
|
||||
@@ -3085,7 +3086,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
if (event.type === "server.instance.disposed") {
|
||||
const props = event.properties as Record<string, unknown> | null
|
||||
const dir = typeof props?.directory === "string" ? props.directory : undefined
|
||||
if (dir && path.resolve(dir) !== path.resolve(this.getWorkspaceDirectory())) return
|
||||
if (dir && !sameDirectory(dir, this.getWorkspaceDirectory())) return
|
||||
void this.reloadAfterAuthChange()
|
||||
return
|
||||
}
|
||||
@@ -3131,8 +3132,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
handleNetworkEvent(event.type as string, event.properties as any, this.client, (s) => this.getWorkspaceDirectory(s))
|
||||
|
||||
if (event.type === "indexing.status" && directory) {
|
||||
const current = path.resolve(this.getWorkspaceDirectory(this.currentSession?.id))
|
||||
if (path.resolve(directory) !== current) return
|
||||
if (!sameDirectory(directory, this.getWorkspaceDirectory(this.currentSession?.id))) return
|
||||
}
|
||||
|
||||
const msg = mapSSEEventToWebviewMessage(event, sessionID)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { prettifyError } from "zod/v4"
|
||||
import type { CloudSessionMessage, IndexingStatus } from "./services/cli-backend/types"
|
||||
import type { PartBatch, PartUpdate } from "./kilo-provider/session-stream-scheduler"
|
||||
import type { PartRemove } from "./shared/stream-messages"
|
||||
import * as path from "path"
|
||||
|
||||
export { SessionStreamScheduler } from "./kilo-provider/session-stream-scheduler"
|
||||
|
||||
@@ -359,6 +360,17 @@ export function resolveNewSessionDirectory(input: {
|
||||
})
|
||||
}
|
||||
|
||||
export function sameDirectory(a: string, b: string): boolean {
|
||||
if (!a || !b) return false
|
||||
|
||||
const left = path.resolve(a)
|
||||
const right = path.resolve(b)
|
||||
if (path.relative(left, right) === "") return true
|
||||
|
||||
if (process.platform !== "win32") return false
|
||||
return path.relative(left.toLowerCase(), right.toLowerCase()) === ""
|
||||
}
|
||||
|
||||
export type WebviewMessage =
|
||||
| PartUpdate
|
||||
| PartBatch
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { formatIndexingLabel, indexingTone } from "../../webview-ui/src/context/indexing-utils"
|
||||
import {
|
||||
applyIndexingStatusMessage,
|
||||
formatIndexingLabel,
|
||||
indexingTone,
|
||||
} from "../../webview-ui/src/context/indexing-utils"
|
||||
import { mapSSEEventToWebviewMessage } from "../../src/kilo-provider-utils"
|
||||
import { configFeatures } from "../../src/features"
|
||||
import type { EventIndexingStatus, IndexingStatus } from "@kilocode/sdk/v2/client"
|
||||
@@ -120,3 +124,54 @@ describe("indexing feature detection", () => {
|
||||
expect(configFeatures({}).indexing).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("indexing status message handling", () => {
|
||||
it("applies indexing status regardless of feature-toggle race", () => {
|
||||
let status = makeStatus()
|
||||
let loading = true
|
||||
const applied = applyIndexingStatusMessage(
|
||||
{
|
||||
type: "indexingStatusLoaded",
|
||||
status: makeStatus({
|
||||
state: "In Progress",
|
||||
message: "Indexing",
|
||||
processedFiles: 5,
|
||||
totalFiles: 20,
|
||||
percent: 25,
|
||||
}),
|
||||
},
|
||||
(next) => {
|
||||
status = next
|
||||
},
|
||||
(next) => {
|
||||
loading = next
|
||||
},
|
||||
)
|
||||
|
||||
expect(applied).toBe(true)
|
||||
expect(status.state).toBe("In Progress")
|
||||
expect(status.percent).toBe(25)
|
||||
expect(loading).toBe(false)
|
||||
})
|
||||
|
||||
it("ignores unrelated extension messages", () => {
|
||||
let status = makeStatus()
|
||||
let loading = true
|
||||
const applied = applyIndexingStatusMessage(
|
||||
{
|
||||
type: "connectionState",
|
||||
state: "connected",
|
||||
},
|
||||
(next) => {
|
||||
status = next
|
||||
},
|
||||
(next) => {
|
||||
loading = next
|
||||
},
|
||||
)
|
||||
|
||||
expect(applied).toBe(false)
|
||||
expect(status.state).toBe("Disabled")
|
||||
expect(loading).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,8 @@ const { KiloProvider } = await import("../../src/KiloProvider")
|
||||
type Internals = {
|
||||
connectionState: "connecting" | "connected" | "disconnected" | "error"
|
||||
currentSession: { id: string } | null
|
||||
cachedIndexingStatusMessage: unknown
|
||||
handleEvent: (event: unknown, directory?: string) => void
|
||||
reloadAfterAuthChange: () => Promise<void>
|
||||
handleUpdateConfig: (partial: Partial<Config>) => Promise<void>
|
||||
fetchAndSendConfig: () => Promise<void>
|
||||
@@ -140,4 +142,42 @@ describe("KiloProvider indexing refresh", () => {
|
||||
globalThis.fetch = original
|
||||
}
|
||||
})
|
||||
|
||||
it("forwards indexing.status when directory only differs by Windows drive casing", () => {
|
||||
const provider = new KiloProvider(
|
||||
{} as never,
|
||||
{
|
||||
resolveEventSessionId: () => undefined,
|
||||
} as never,
|
||||
)
|
||||
const internal = provider as unknown as Internals
|
||||
provider.setSessionDirectory("ses_worktree", "C:/Repo/Work")
|
||||
internal.currentSession = { id: "ses_worktree" }
|
||||
|
||||
const desc = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
|
||||
try {
|
||||
internal.handleEvent(
|
||||
{
|
||||
type: "indexing.status",
|
||||
properties: {
|
||||
status: {
|
||||
state: "Complete",
|
||||
message: "Done",
|
||||
processedFiles: 10,
|
||||
totalFiles: 10,
|
||||
percent: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
"c:/repo/work",
|
||||
)
|
||||
} finally {
|
||||
if (desc) Object.defineProperty(process, "platform", desc)
|
||||
}
|
||||
|
||||
const msg = internal.cachedIndexingStatusMessage as { type?: string; status?: { state?: string } } | undefined
|
||||
expect(msg?.type).toBe("indexingStatusLoaded")
|
||||
expect(msg?.status?.state).toBe("Complete")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { afterEach, describe, expect, it } from "bun:test"
|
||||
import { sameDirectory } from "../../src/kilo-provider-utils"
|
||||
|
||||
const platform = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
|
||||
function setPlatform(value: string) {
|
||||
Object.defineProperty(process, "platform", { value, configurable: true })
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
if (platform) Object.defineProperty(process, "platform", platform)
|
||||
})
|
||||
|
||||
describe("sameDirectory", () => {
|
||||
it("matches identical paths", () => {
|
||||
expect(sameDirectory("/repo/pkg", "/repo/pkg")).toBe(true)
|
||||
})
|
||||
|
||||
it("matches trailing slash differences", () => {
|
||||
expect(sameDirectory("/repo/pkg", "/repo/pkg/")).toBe(true)
|
||||
})
|
||||
|
||||
it("matches Windows drive-letter case differences", () => {
|
||||
setPlatform("win32")
|
||||
expect(sameDirectory("C:/Repo/Work", "c:/repo/work")).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false for different directories", () => {
|
||||
expect(sameDirectory("/repo/pkg-a", "/repo/pkg-b")).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IndexingStatus } from "../types/messages"
|
||||
import type { ExtensionMessage, IndexingStatus } from "../types/messages"
|
||||
|
||||
export function formatIndexingLabel(status: IndexingStatus): string {
|
||||
if (status.state === "In Progress") {
|
||||
@@ -24,3 +24,14 @@ export function indexingTone(status: IndexingStatus): "muted" | "warning" | "suc
|
||||
if (status.state === "Standby") return "muted"
|
||||
return "muted"
|
||||
}
|
||||
|
||||
export function applyIndexingStatusMessage(
|
||||
message: ExtensionMessage,
|
||||
setStatus: (status: IndexingStatus) => void,
|
||||
setLoading: (value: boolean) => void,
|
||||
): boolean {
|
||||
if (message.type !== "indexingStatusLoaded") return false
|
||||
setStatus(message.status)
|
||||
setLoading(false)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createContext, useContext, createMemo, createSignal, onCleanup, createE
|
||||
import type { ParentComponent, Accessor } from "solid-js"
|
||||
import { useVSCode } from "./vscode"
|
||||
import type { ExtensionMessage, IndexingStatus } from "../types/messages"
|
||||
import { formatIndexingLabel, indexingTone } from "./indexing-utils"
|
||||
import { applyIndexingStatusMessage, formatIndexingLabel, indexingTone } from "./indexing-utils"
|
||||
import { useConfig } from "./config"
|
||||
|
||||
interface IndexingContextValue {
|
||||
@@ -31,10 +31,7 @@ export const IndexingProvider: ParentComponent = (props) => {
|
||||
const [loading, setLoading] = createSignal(true)
|
||||
|
||||
const unsubscribe = vscode.onMessage((message: ExtensionMessage) => {
|
||||
if (!features().indexing) return
|
||||
if (message.type !== "indexingStatusLoaded") return
|
||||
setStatus(message.status)
|
||||
setLoading(false)
|
||||
applyIndexingStatusMessage(message, setStatus, setLoading)
|
||||
})
|
||||
|
||||
onCleanup(unsubscribe)
|
||||
|
||||
Reference in New Issue
Block a user