1// Enough hits to look past same-named neighbours (`ChatView.test.tsx`) without
2// asking for a full listing on a single click.
3export const WORKSPACE_BASENAME_LOOKUP_LIMIT = 25;
4
5// One counter for every caller: they all open the same panel, so the newest
6// click wins regardless of which one started the lookup.
7let latestLookupSequence = 0;
8
9/** Call the returned predicate when the search settles; false means a later click superseded it. */
10export function claimWorkspaceBasenameLookup(): () => boolean {
11 latestLookupSequence += 1;
12 const claimed = latestLookupSequence;
13 return () => claimed === latestLookupSequence;
14}
15
16export interface WorkspaceEntryCandidate {
17 readonly path: string;
18 readonly kind: "file" | "directory";
19}
20
21function basenameOfPath(path: string): string {
22 const separatorIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
23 return separatorIndex >= 0 ? path.slice(separatorIndex + 1) : path;
24}
25
26export function needsWorkspaceBasenameLookup(relativePath: string): boolean {
27 const trimmed = relativePath.trim();
28 return (
29 trimmed !== "." &&
30 trimmed !== ".." &&
31 trimmed.length > 0 &&
32 !trimmed.includes("/") &&
33 !trimmed.includes("\\")
34 );
35}
36
37export function pickWorkspaceBasenameMatch(
38 basename: string,
39 entries: ReadonlyArray<WorkspaceEntryCandidate>,
40): string | null {
41 const target = basename.trim();
42 if (!target) return null;
43 const files = entries.filter((entry) => entry.kind === "file");
44 const exact = files.find((entry) => basenameOfPath(entry.path) === target);
45 if (exact) return exact.path;
46 // Folded matching covers casing that drifted from disk, but `FOO.ts` against
47 // both `Foo.ts` and `foo.ts` has no right answer, so it resolves to nothing
48 // rather than opening whichever the index ranked first.
49 const folded = target.toLowerCase();
50 const foldedMatches = files.filter(
51 (entry) => basenameOfPath(entry.path).toLowerCase() === folded,
52 );
53 return foldedMatches.length === 1 ? (foldedMatches[0]?.path ?? null) : null;
54}