Files
qwen-code/packages/vscode-ide-companion/src/webview/hooks/file/useFileContext.ts
yiliang114 f5306339f6 refactor(vscode-ide-companion/types): move ApprovalModeValue type to dedicated file
feat(vscode-ide-companion/file-context): improve file context handling and search

Enhance file context hook to better handle search queries and reduce redundant requests.
Track last query to optimize when to refetch full file list.
Improve logging for debugging purposes.
2025-12-13 09:19:09 +08:00

145 lines
3.6 KiB
TypeScript

/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useCallback, useRef } from 'react';
import type { VSCodeAPI } from '../../hooks/useVSCode.js';
/**
* File context management Hook
* Manages active file, selection content, and workspace file list
*/
export const useFileContext = (vscode: VSCodeAPI) => {
const [activeFileName, setActiveFileName] = useState<string | null>(null);
const [activeFilePath, setActiveFilePath] = useState<string | null>(null);
const [activeSelection, setActiveSelection] = useState<{
startLine: number;
endLine: number;
} | null>(null);
const [workspaceFiles, setWorkspaceFiles] = useState<
Array<{
id: string;
label: string;
description: string;
path: string;
}>
>([]);
// File reference mapping: @filename -> full path
const fileReferenceMap = useRef<Map<string, string>>(new Map());
// Whether workspace files have been requested
const hasRequestedFilesRef = useRef(false);
// Last non-empty query to decide when to refetch full list
const lastQueryRef = useRef<string | undefined>(undefined);
// Search debounce timer
const searchTimerRef = useRef<NodeJS.Timeout | null>(null);
/**
* Request workspace files
*/
const requestWorkspaceFiles = useCallback(
(query?: string) => {
const normalizedQuery = query?.trim();
// If there's a query, clear previous timer and set up debounce
if (normalizedQuery && normalizedQuery.length >= 1) {
if (searchTimerRef.current) {
clearTimeout(searchTimerRef.current);
}
searchTimerRef.current = setTimeout(() => {
vscode.postMessage({
type: 'getWorkspaceFiles',
data: { query: normalizedQuery },
});
}, 300);
lastQueryRef.current = normalizedQuery;
} else {
// For empty query, request once initially and whenever we are returning from a search
const shouldRequestFullList =
!hasRequestedFilesRef.current || lastQueryRef.current !== undefined;
if (shouldRequestFullList) {
lastQueryRef.current = undefined;
hasRequestedFilesRef.current = true;
vscode.postMessage({
type: 'getWorkspaceFiles',
data: {},
});
}
}
},
[vscode],
);
/**
* Add file reference
*/
const addFileReference = useCallback((fileName: string, filePath: string) => {
fileReferenceMap.current.set(fileName, filePath);
}, []);
/**
* Get file reference
*/
const getFileReference = useCallback(
(fileName: string) => fileReferenceMap.current.get(fileName),
[],
);
/**
* Clear file references
*/
const clearFileReferences = useCallback(() => {
fileReferenceMap.current.clear();
}, []);
/**
* Request active editor info
*/
const requestActiveEditor = useCallback(() => {
vscode.postMessage({ type: 'getActiveEditor', data: {} });
}, [vscode]);
/**
* Focus on active editor
*/
const focusActiveEditor = useCallback(() => {
vscode.postMessage({
type: 'focusActiveEditor',
data: {},
});
}, [vscode]);
return {
// State
activeFileName,
activeFilePath,
activeSelection,
workspaceFiles,
hasRequestedFiles: hasRequestedFilesRef.current,
// State setters
setActiveFileName,
setActiveFilePath,
setActiveSelection,
setWorkspaceFiles,
// File reference operations
addFileReference,
getFileReference,
clearFileReferences,
// Operations
requestWorkspaceFiles,
requestActiveEditor,
focusActiveEditor,
};
};