mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(explorer): keep filename search results complete
This commit is contained in:
@@ -270,7 +270,7 @@ describe('useRuntimeFileListForWorktree', () => {
|
||||
worktreeId: workspaceKey,
|
||||
worktreePath: '/srv/platform'
|
||||
}),
|
||||
expect.objectContaining({ query: 'remote-folder', limit: 32 })
|
||||
expect.objectContaining({ query: 'remote-folder', limit: QUICK_OPEN_LISTING_MAX_RESULTS })
|
||||
)
|
||||
expect(listRuntimeFilesMock).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
@@ -402,7 +402,7 @@ describe('useRuntimeFileListForWorktree', () => {
|
||||
}),
|
||||
{
|
||||
query: 'sta-4354-target',
|
||||
limit: 32,
|
||||
limit: QUICK_OPEN_LISTING_MAX_RESULTS,
|
||||
excludePaths: undefined,
|
||||
signal: expect.any(AbortSignal)
|
||||
}
|
||||
@@ -566,7 +566,8 @@ describe('useRuntimeFileListForWorktree', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('does not restart local listings when only the query changes', async () => {
|
||||
it('searches local workspaces by query instead of reusing the capped inventory', async () => {
|
||||
vi.useFakeTimers()
|
||||
const workspaceKey = folderWorkspaceKey('folder-workspace-1')
|
||||
useAppStore.setState({
|
||||
folderWorkspaces: [makeFolderWorkspace()],
|
||||
@@ -575,26 +576,25 @@ describe('useRuntimeFileListForWorktree', () => {
|
||||
worktreesByRepo: {}
|
||||
} as Partial<AppState>)
|
||||
|
||||
const root = await renderProbe({
|
||||
enabled: true,
|
||||
onState: () => {},
|
||||
query: 'one',
|
||||
worktreeId: workspaceKey
|
||||
})
|
||||
await waitForListRuntimeFilesCall()
|
||||
try {
|
||||
await renderProbe({
|
||||
enabled: true,
|
||||
onState: () => {},
|
||||
query: 'AppDelegate.swift',
|
||||
worktreeId: workspaceKey
|
||||
})
|
||||
await act(async () => vi.advanceTimersByTimeAsync(120))
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
createElement(HookProbe, {
|
||||
enabled: true,
|
||||
onState: () => {},
|
||||
query: 'two',
|
||||
worktreeId: workspaceKey
|
||||
expect(searchRuntimeFilePathsMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ worktreePath: '/srv/platform' }),
|
||||
expect.objectContaining({
|
||||
query: 'AppDelegate.swift',
|
||||
limit: QUICK_OPEN_LISTING_MAX_RESULTS
|
||||
})
|
||||
)
|
||||
})
|
||||
await flushEffects()
|
||||
|
||||
expect(listRuntimeFilesMock).toHaveBeenCalledTimes(1)
|
||||
expect(listRuntimeFilesMock).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,6 +26,8 @@ export type RuntimeFileListState = {
|
||||
loading: boolean
|
||||
loadError: string | null
|
||||
truncated?: boolean
|
||||
/** Query that produced `files`; null means a request is still settling. */
|
||||
resolvedQuery?: string | null
|
||||
operationOwner?: FileExplorerOperationOwner
|
||||
}
|
||||
|
||||
@@ -145,6 +147,7 @@ export function useRuntimeFileListForWorktree({
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [loadError, setLoadError] = useState<string | null>(null)
|
||||
const [truncated, setTruncated] = useState(false)
|
||||
const [resolvedQuery, setResolvedQuery] = useState<string | null | undefined>(undefined)
|
||||
const [listedOperationOwner, setListedOperationOwner] = useState<FileExplorerOperationOwner>({
|
||||
kind: 'unresolved'
|
||||
})
|
||||
@@ -185,8 +188,7 @@ export function useRuntimeFileListForWorktree({
|
||||
activeTargetStatus === 'connecting' ||
|
||||
activeTargetStatus === 'deploying-relay' ||
|
||||
activeTargetStatus === 'reconnecting'
|
||||
const usesRuntimePathSearch =
|
||||
(runtimeEnvironmentId !== null || connectionId !== undefined) && query !== undefined
|
||||
const usesRuntimePathSearch = query !== undefined && operationRouteAvailable
|
||||
const remoteQuery = usesRuntimePathSearch ? query.trim() : ''
|
||||
const remoteQueryTooLarge = usesRuntimePathSearch && isQuickOpenRemoteQueryTooLarge(remoteQuery)
|
||||
const requestKey = useMemo(
|
||||
@@ -206,6 +208,7 @@ export function useRuntimeFileListForWorktree({
|
||||
if (!enabled) {
|
||||
setLoading(false)
|
||||
setTruncated(false)
|
||||
setResolvedQuery(null)
|
||||
setListedOperationOwner({ kind: 'unresolved' })
|
||||
return
|
||||
}
|
||||
@@ -216,6 +219,7 @@ export function useRuntimeFileListForWorktree({
|
||||
setLoadError(operationRouteAvailable ? null : getFileExplorerOwnerUnresolvedMessage())
|
||||
setLoading(false)
|
||||
setTruncated(false)
|
||||
setResolvedQuery(null)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -223,6 +227,7 @@ export function useRuntimeFileListForWorktree({
|
||||
const requestKeyChanged = lastRequestKeyRef.current !== requestKey
|
||||
if (requestKeyChanged) {
|
||||
setFiles([])
|
||||
setResolvedQuery(null)
|
||||
}
|
||||
lastRequestKeyRef.current = requestKey
|
||||
setLoadError(null)
|
||||
@@ -231,6 +236,7 @@ export function useRuntimeFileListForWorktree({
|
||||
if (usesRuntimePathSearch && (remoteQuery.length === 0 || remoteQueryTooLarge)) {
|
||||
setFiles([])
|
||||
setLoading(false)
|
||||
setResolvedQuery(remoteQuery)
|
||||
setListedOperationOwner(operationOwnerRef.current)
|
||||
return
|
||||
}
|
||||
@@ -252,7 +258,7 @@ export function useRuntimeFileListForWorktree({
|
||||
? debounceRuntimeFilePathSearch(120, requestAbortController.signal, () =>
|
||||
searchRuntimeFilePaths(requestContext, {
|
||||
query: remoteQuery,
|
||||
limit: 32,
|
||||
limit: QUICK_OPEN_LISTING_MAX_RESULTS,
|
||||
excludePaths,
|
||||
...(connectionId ? { requestToken } : {}),
|
||||
signal: requestAbortController.signal
|
||||
@@ -277,6 +283,7 @@ export function useRuntimeFileListForWorktree({
|
||||
if (!cancelled) {
|
||||
setFiles(result.files)
|
||||
setTruncated(result.truncated)
|
||||
setResolvedQuery(usesRuntimePathSearch ? remoteQuery : undefined)
|
||||
setListedOperationOwner(requestOperationOwner)
|
||||
}
|
||||
})
|
||||
@@ -284,6 +291,7 @@ export function useRuntimeFileListForWorktree({
|
||||
if (!cancelled) {
|
||||
setFiles([])
|
||||
setTruncated(false)
|
||||
setResolvedQuery(usesRuntimePathSearch ? remoteQuery : null)
|
||||
setLoadError(cleanRuntimeFileListError(error))
|
||||
}
|
||||
})
|
||||
@@ -323,6 +331,7 @@ export function useRuntimeFileListForWorktree({
|
||||
loading: loading || connectionPending,
|
||||
loadError,
|
||||
truncated,
|
||||
resolvedQuery,
|
||||
operationOwner: listedOperationOwner
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,9 +56,13 @@ export function useFileExplorerNameFilter({
|
||||
operationOwner: nameFilterFiles.operationOwner,
|
||||
relativePaths: nameFilterQueryTooLarge
|
||||
? []
|
||||
: nameFilterFiles.loading && nameFilterFiles.files.length === 0
|
||||
? null
|
||||
: nameFilterFiles.files
|
||||
: nameFilterFiles.resolvedQuery === nameFilterQuery.trim()
|
||||
? nameFilterFiles.loading
|
||||
? null
|
||||
: nameFilterFiles.files
|
||||
: nameFilterFiles.loading
|
||||
? null
|
||||
: []
|
||||
}
|
||||
: null,
|
||||
[
|
||||
@@ -66,6 +70,7 @@ export function useFileExplorerNameFilter({
|
||||
nameFilterFiles.files,
|
||||
nameFilterFiles.loading,
|
||||
nameFilterFiles.operationOwner,
|
||||
nameFilterFiles.resolvedQuery,
|
||||
nameFilterQuery,
|
||||
nameFilterQueryTooLarge
|
||||
]
|
||||
|
||||
@@ -205,6 +205,33 @@ describe('runtime file client', () => {
|
||||
expect(runtimeEnvironmentCall).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('searches local workspaces without requiring a connection owner', async () => {
|
||||
fsListFiles.mockResolvedValue(['src/AppDelegate.swift', 'tests/AppDelegate.swift'])
|
||||
|
||||
await expect(
|
||||
searchRuntimeFilePaths(
|
||||
{
|
||||
settings: { activeRuntimeEnvironmentId: null },
|
||||
worktreeId: 'wt-1',
|
||||
worktreePath: '/repo'
|
||||
},
|
||||
{ query: 'AppDelegate.swift', limit: 20_001 }
|
||||
)
|
||||
).resolves.toEqual({
|
||||
files: ['src/AppDelegate.swift', 'tests/AppDelegate.swift'],
|
||||
truncated: false
|
||||
})
|
||||
|
||||
expect(fsListFiles).toHaveBeenCalledWith({
|
||||
rootPath: '/repo',
|
||||
connectionId: undefined,
|
||||
excludePaths: undefined,
|
||||
requestToken: undefined,
|
||||
maxResults: 20_002,
|
||||
searchQuery: 'AppDelegate.swift'
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to one cached legacy inventory and ranks evolving queries locally', async () => {
|
||||
replaceRuntimeEnvironmentRevisions([{ id: 'env-1', createdAt: 1 }])
|
||||
runtimeEnvironmentCall.mockImplementation(({ method }) => {
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function searchRuntimeFilePaths(
|
||||
): Promise<{ files: string[]; truncated: boolean }> {
|
||||
const target = getActiveRuntimeTarget(context.settings)
|
||||
if (target.kind !== 'environment') {
|
||||
if (!context.connectionId || !context.worktreePath) {
|
||||
if (!context.worktreePath) {
|
||||
return { files: [], truncated: false }
|
||||
}
|
||||
const limit = args.limit ?? 32
|
||||
@@ -108,7 +108,7 @@ export async function searchRuntimeFilePaths(
|
||||
return { files: [], truncated: false }
|
||||
}
|
||||
const worktreeSelector = toRuntimeWorktreeSelector(context.worktreeId)
|
||||
const limit = args.limit ?? 32
|
||||
const limit = Math.min(args.limit ?? 32, 32)
|
||||
if (hasCachedLegacyQuickOpenInventory(target, worktreeSelector, context.worktreePath)) {
|
||||
return searchLegacyQuickOpenInventory({
|
||||
target,
|
||||
|
||||
Reference in New Issue
Block a user